Skip to content

设计疑问 #898

@cn-src

Description

@cn-src

对以下问题有疑问:

我看了下 SaInterceptor 的实现,以及一些校验注解,有一些几点问题:

SaInterceptor 里是先校验注解,后校验 run 理的逻辑。在我的一个场景中,我需要校验登录和 apikey 二选一认证即可,认证后我需要初始化上下文,如:登录后存数据到 session ,登录和apikey 的话我会存数据到 ThreadLocal 中。

这种情况分2步,一个是实现认证的二选一,一个是实现上下文数据的存放。

用注解实现,但存在一个问题:在这我需要同时使用 @SaCheckLogin@SaCheckApiKey 注解,否则无法二选一,因为注解先行校验。那我后续还需要用代码逻辑实现拦截其他所有请求(因为我不可能每个接口都加 @SaCheckLogin),但我需要部分接口加 @SaCheckApiKey(明确指出哪些接口可开放),但为了避免重复校验,以及部分是通过 apikey 认证的,我需要做一些逻辑判断,但最大的问题在于我的判断【**无法基于明确的条件,只能用异常捕获打逻辑补丁】。**如下代码:

// 测试:只有通过登录校验,或者提供了正确的 ApiKey,才可以进入方法
@RequestMapping("/test")
@SaCheckOr(login = @SaCheckLogin, append = { SaCheckApiKey.class })
@SaCheckApiKey
public SaResult test() {
    // ...
    return SaResult.ok();
}
package com.idataway.pro.core.modules.security.config

import cn.dev33.satoken.annotation.SaCheckLogin
import cn.dev33.satoken.annotation.SaCheckOr
import cn.dev33.satoken.apikey.annotation.SaCheckApiKey
import cn.dev33.satoken.apikey.exception.ApiKeyException
import cn.dev33.satoken.apikey.model.ApiKeyModel
import cn.dev33.satoken.apikey.template.SaApiKeyUtil
import cn.dev33.satoken.interceptor.SaInterceptor
import cn.dev33.satoken.stp.StpUtil
import com.idataway.pro.core.entity.User
import com.idataway.pro.core.entity.query.QMenu.Alias.permission
import com.idataway.pro.core.modules.security.LoggedUser
import com.idataway.pro.core.support.config.TenantContext
import com.idataway.pro.core.support.config.WebContext
import com.idataway.pro.core.support.config.WebContext.Companion.LOGGED_USER
import org.springframework.context.annotation.Configuration
import org.springframework.web.method.HandlerMethod
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
import java.util.UUID


/**
 * @author zhangpeng
 */
@Configuration
class SaTokenConfigurer : WebMvcConfigurer {

    override fun addInterceptors(registry: InterceptorRegistry) {
        // 注册Sa-Token的路由拦截器
        registry.addInterceptor(SaInterceptor { handler: Any? ->
            // 由于注解是先进行校验,因此如果想实现:手动登录以及 apikey 认证二选一的效果,需在接口如下配置:
            // @SaCheckOr(login = [SaCheckLogin()], append = [SaCheckApiKey::class])
            // @SaCheckApiKey

            val handlerMethod = handler as? HandlerMethod
            val apikey = handlerMethod?.method?.isAnnotationPresent(SaCheckApiKey::class.java)

            // 未配置 SaCheckApiKey 的接口,执行登录校验。
            if (apikey == null || !apikey) {
                StpUtil.checkLogin()
            }
            else {
                try {
                    // 初始化基于 apiKey 的登录状态的相关上下文信息。
                    val userId = SaApiKeyUtil.currentApiKey().loginId as UUID
                    val user = User.find.byId(userId)!!
                    // TODO permissions 和 scope 待完善
                    WebContext.loggedUser(LoggedUser(user, listOf()))
                    TenantContext.setTenant(WebContext.loggedUsername())
                } catch (e: ApiKeyException) {
                    // 由于注解已经进行过 apiKey 的校验,因此这里直接忽略异常,因为有可能其通过手动登录。
                }
            }
            // 如果用户已登录,初始化相关上下文信息。
            if (StpUtil.isLogin()) {
                WebContext.loggedUser(StpUtil.getSession().getModel(LOGGED_USER, LoggedUser::class.java))
                TenantContext.setTenant(WebContext.loggedUsername())
            }
        }).addPathPatterns("/**")
            .excludePathPatterns(
                listOf(
                    "/login",
                    "/error",
                    "/v3/api-docs",
                    "/public/**"
                )
            )

    }
}

还有一个问题:像 @SaCheckLogin @SaCheckApiKey 这种注解,属于同一场景的注解,不存在说需要同时做两种认证,像 SaCheckOr 这种就不应该用于这类。认证场景应该是多选一,权限范围则是存在【且】和【或】。我觉得最好将【认证】和【权限】分开。像认证类,天然就应该过一道即可。而权限范围这类(角色,权限值,scope)才存在布尔逻辑。我也无法直接将注解校验关闭,因像权限类一般用注解是比较好的。

像 spring security 框架我没记错的话,应该是:认证逻辑用代码扩展,没提供注解,并且过了一个认证点即可,权限值和角色,这类需要细粒度控制的才支持注解。这个框架设计较为一体化,内部流程处理的比较好,但的确不容易上手以及扩展。sa-token 则更偏向于util ,流程需要自己组装,但这种安全框架,一旦组装不好,就容易出现翻车,很容易就被绕开。

初步的话我觉得做以下调整会比较好:

  • SaCheckOr 不用于认证,只用于权限。
  • 可以自定义多个认证点,但认证多选一即可。
  • SaCheckApiKey 注解,将 key 的认证和 scope 权限分开。
  • isAnnotation 明确指定忽略的注解,或不提供,万一被当成临时工具,临时改了,没还原就凉了。

< 备注:请尽量详细描述问题所在 >

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions