dromara/Sa-Token

为什么 login 后 StpUtil.getTokenInfo() 未false

Closed this issue · 5 comments

对以下问题有疑问:

  • 一个从未登录过的用户没有token情况下去请求登录接口
  • 然后我在apifox测试请求

相关代码:

        // 第一步:比对前端提交的账号名称、密码
        if ("zhang" == name/* && "123456" == pwd*/) {
            // 第二步:根据账号id,进行登录
            StpUtil.login(10001)
            val tokenInfo = StpUtil.getTokenInfo()

            // Step 3: 返回 token 信息到前端
            return SaResult.data(tokenInfo)
        }
        return SaResult.error("登录失败")

apifox返回:

{
    "code": 200,
    "msg": "ok",
    "data": {
        "tokenName": "satoken",
        "isLogin": false,
        "loginType": "login",
        "tokenTimeout": -2,
        "sessionTimeout": -2,
        "tokenSessionTimeout": -2,
        "tokenActiveTimeout": -1
    }
}

日志输出:
SA [INFO] -->: 账号 10001 登录成功 (loginType=login), 会话凭证 token=511971cd-8909-4ae8-a80d-52fc1147850d
POST - /salogin/test - 200 OK - 1.6291ms

最后,我没法从StpUtil.getTokenInfo()获取token交给前端

我用其他冷门框架+javalin搭配,没有使用Spring,自定义实现Context,写入SaConfig配置
不知道漏了什么步骤

自定义的实现贴出来看看

自定义的实现贴出来看看

import cn.dev33.satoken.context.SaTokenContext
import cn.dev33.satoken.context.model.SaRequest
import cn.dev33.satoken.context.model.SaResponse
import cn.dev33.satoken.context.model.SaStorage
import io.javalin.http.Context

class SaTokenContextImpl(val context: Context): SaTokenContext {

    override fun getRequest(): SaRequest {
        return SaRequestImpl(context)
    }

    override fun getResponse(): SaResponse {
        return SaResponseImpl(context)
    }

    override fun getStorage(): SaStorage {
        return SaStorageImpl(context)
    }

    // 实现 matchPath,判断路径是否匹配
    override fun matchPath(pattern: String?, path: String?): Boolean {
        if (pattern == null || path == null) return false
        // 使用 Sa-Token 的路径匹配规则,或自定义路径匹配逻辑
        return path.matches(pattern.toRegex())
    }

}
import cn.dev33.satoken.context.model.SaRequest
import io.javalin.http.Context

class SaRequestImpl(val context: Context): SaRequest {

    override fun getSource(): Context {
        return context
    }

    override fun getParam(name: String): String? {
        return context.queryParam(name)
    }

    override fun getParamNames(): MutableList<String> {
        return context.queryParamMap().keys.toMutableList()
    }

    override fun getParamMap(): MutableMap<String, String> {
        // 将 Javalin 的 Map<String, List<String>> 转换为 Sa-Token 所需的 Map<String, String>
        return context.queryParamMap().mapValues { it.value.firstOrNull().orEmpty() }.toMutableMap()
    }

    override fun getHeader(name: String): String? {
        return context.header(name) ?: ""
    }

    override fun getCookieValue(name: String): String? {
        return context.cookie(name) ?: ""
    }

    override fun getCookieFirstValue(name: String): String? {
        return context.cookie(name)?.split(";")?.firstOrNull() ?: ""
    }

    override fun getCookieLastValue(name: String): String? {
        return context.cookie(name)?.split(";")?.lastOrNull() ?: ""
    }

    override fun getRequestPath(): String {
        return context.matchedPath()
    }

    override fun getUrl(): String {
        return context.url()
    }

    override fun getMethod(): String {
        // 获取实际的 HTTP 方法,或者使用 X-HTTP-Method-Override 头的值
        val handlerType = context.method()
        return handlerType.name  // 转换 HandlerType 为字符串
    }


    override fun forward(path: String) {
        // 执行重定向操作
        context.redirect(path)
    }
}
import cn.dev33.satoken.context.model.SaStorage
import io.javalin.http.Context

class SaStorageImpl(private val context: Context) : SaStorage {

    // 创建一个存储会话数据的映射
    private val storageMap: MutableMap<String, Any?> = mutableMapOf()

    // 获取存储中某个键的值
    override fun get(key: String): Any? {
        return storageMap[key]
    }

    // 设置存储中的某个键值对
    override fun set(key: String?, value: Any?): SaStorage {
        if (key != null) {
            storageMap[key] = value
        }
        return this
    }

    // 删除存储中的某个键值对
    override fun delete(key: String?): SaStorage {
        if (key != null) {
            storageMap.remove(key)
        }
        return this
    }

    // 返回存储的源对象,在这里我们可以直接返回 Javalin 的 Context
    override fun getSource(): Any {
        return context
    }
}
import cn.dev33.satoken.context.model.SaResponse
import io.javalin.http.Context

class SaResponseImpl(private val context: Context) : SaResponse {

    // 返回 Javalin 的 Context 作为源对象,供 Sa-Token 使用
    override fun getSource(): Any {
        return context
    }

    // 设置 HTTP 响应状态码
    override fun setStatus(statusCode: Int): SaResponse {
        context.status(statusCode)
        return this
    }

    // 设置 HTTP 响应头(覆盖现有值)
    override fun setHeader(name: String?, value: String?): SaResponse {
        if (name != null && value != null) {
            context.header(name, value)
        }
        return this
    }

    // 添加 HTTP 响应头(不会覆盖已有值)
    override fun addHeader(name: String?, value: String?): SaResponse {
        if (name != null && value != null) {
            context.header(name, context.header(name) + ", " + value)
        }
        return this
    }

    // 执行重定向到指定 URL
    override fun redirect(location: String?): Any {
        return context.redirect(location)
    }
}
server.before {
    val saTokenContext = SaTokenContextImpl(it)
    SaManager.setSaTokenContext(saTokenContext)
}

没看出来哪有问题,不过猜测可能是 SaStorage 的实现有点问题,导致登录时没有把token成功保存到上下文,导致后续再获取也没有获取到

感谢定位问题,确实SaStorage实现有错

我又重新打开了,因为测试登录后,请求查询是否登录时,结果为false

@RouterNode("/isLogin")
    fun isLogin(): SaResult {
        return SaResult.ok("是否登录:${StpUtil.isLogin()}")
    }

apifox都测试过cookie和bearer token,均请求为false。(登录请求后还没有注销)

已解决,请求头要对应tokenName去写Bearer token