```xml
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-solon-plugin</artifactId>
    <version>1.45.0</version>
</dependency>
```


### 1、描述

签权扩展插件，对 sa-token（[代码仓库](https://gitee.com/dromara/sa-token)）签权框架进行适配。更多信息可见：[框架官网](https://sa-token.cc/)


sa-token 的主要缓存扩展包（引入一个后，通过配置构建）：


| 扩展包                                      | 说明                                            |  主要类       | 
| ------------------------- | ---------------------------- |  -------- | 
| `cn.dev33:sa-token-redisx`        | 集成 RedisX 客户端                          |  SaTokenDaoForRedisx | 
| `cn.dev33:sa-token-redisson`     | 集成 Redisson 客户端                        | SaTokenDaoForRedisson  | 
| `cn.dev33:sa-token-caffeine`     | 集成 Caffeine 缓存方案（基于内存）     | SaTokenDaoForCaffeine  | 


sa-token 的主要序列化扩展包（引入一个后，自动可用）：

| 扩展包 | 说明 | 
| -------- | -------- | 
| `cn.dev33:sa-token-snack3`        | 集成 snack3 序列化框架     | 
| `cn.dev33:sa-token-snack4`        | 集成 snack4 序列化框架     | 
| `cn.dev33:sa-token-jackson`       | 集成 jackson 序列化框架     | 
| `cn.dev33:sa-token-fastjson`       | 集成 fastjson 序列化框架     | 
| `cn.dev33:sa-token-fastjson2`     | 集成 fastjson2 序列化框架     | 



### 2、拦截处理的适配对比



| 拦截类 | 实现机制 | 备注 |
| -------- | -------- | -------- |
| SaTokenFilter     | 基于 Filter 实现（对静态文件有控制权）     | v1.12.3 后支持     |
| SaTokenInterceptor     | 基于 RouterInterceptor 实现（只对动态路由有效）     | v1.12.3 后支持     |


要了解它们的作用范围，可以看下[《请求处理过程示意图》](/article/242)。


### 3、对接使用示例

* 配置：

```yml
# sa-token配置
sa-token:
  # token名称 (同时也是cookie名称)
  token-name: satoken
  # token有效期，单位s 默认30天, -1代表永不过期
  timeout: 2592000
  # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
  active-timeout: -1
  # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
  is-concurrent: true #旧版曾用名（allow-concurrent-login）
  # 在多人登录同一账号时，是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
  is-share: true
  # token风格
  token-style: uuid
  # 是否输出操作日志
  is-log: false
```

* 对接代码：

1）添加拦截器（用于支持"路径规则"和"注解处理"，支持 @SaIgnore 注解）：

```java
@Configuration
public class Config {
  @Bean(index = -100) //-100，是顺序位（低值优先）
  public SaTokenInterceptor saTokenInterceptor() {
     return new SaTokenInterceptor(); //用于支持规划处理及注解处理
  }
}
```

2）一般，我们会在 SaTokenInterceptor 上增加一些必要的路由规则和一些必要设定：

```java
@Configuration
public class Config {
  @Bean(index = -100)  //-100，是顺序位（低值优先）
  public SaTokenInterceptor saTokenInterceptor() {
     return new SaTokenInterceptor()
        // 指定 [拦截路由] 与 [放行路由]
        .addInclude("/**").addExclude("/favicon.ico")

        // 认证函数: 每次请求执行
        .setAuth(req -> {
          // System.out.println("---------- sa全局认证");
          SaRouter.match("/**", StpUtil::checkLogin);

          // 根据路由划分模块，不同模块不同鉴权
          SaRouter.match("/user/**", r -> StpUtil.checkPermission("user"));
          SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
        })

        // 异常处理函数：每次认证函数发生异常时执行此函数 //包括注解异常
        .setError(e -> {
          System.out.println("---------- sa全局异常 ");
          return AjaxJson.getError(e.getMessage());
        })

        // 前置函数：在每次认证函数之前执行
        .setBeforeAuth(req -> {
          // ---------- 设置一些安全响应头 ----------
          SaHolder.getResponse()
              // 服务器名称
              .setServer("sa-server")
              // 是否可以在iframe显示视图： DENY=不可以 | SAMEORIGIN=同域下可以 | ALLOW-FROM uri=指定域名下可以
              .setHeader("X-Frame-Options", "SAMEORIGIN")
              // 是否启用浏览器默认XSS防护： 0=禁用 | 1=启用 | 1; mode=block 启用, 并在检查到XSS攻击时，停止渲染页面
              .setHeader("X-XSS-Protection", "1; mode=block")
              // 禁用浏览器内容嗅探
              .setHeader("X-Content-Type-Options", "nosniff");
        });
  }
}
```

3）它还可以支持“注解”控制权限：


```java
// 登录认证：只有登录之后才能进入该方法 
@SaCheckLogin                        
@Mapping("info")
public String info() {
    return "查询用户信息";
}

// 角色认证：必须具有指定角色才能进入该方法 
@SaCheckRole("super-admin")        
@Mapping("add")
public String add() {
    return "用户增加";
}

// 权限认证：必须具有指定权限才能进入该方法 
@SaCheckPermission("user-add")        
@Mapping("add")
public String add() {
    return "用户增加";
}
```


### 4、Sa Token Redis Dao 使用示例

* 基于 redisx 的应用

添加依赖包（更多的 redis 适配扩展，[参考 sa-token 官网](https://sa-token.cc/doc.html#/plugin/dao-extend)）：

```xml
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-redisx</artifactId>
    <version>最新版</version>
</dependency>

<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-snack3</artifactId>
    <version>最新版</version>
</dependency>
```



添加配置：

```
sa-token-dao: #名字可以随意取
  redis:
    server: "localhost:6379"
    password: 123456
    db: 1
    maxTotal: 200
```

添加构建代码：

```java
@Configuration
public class Config {
    @Bean
    public SaTokenDao saTokenDaoInit(@Inject("${sa-token-dao.redis}") SaTokenDaoForRedisx saTokenDao) {
        return saTokenDao;
    }
}
```

代码示例：

 [https://gitee.com/noear/solon-examples/tree/main/3.Solon-Web/demo3032-auth_sa_token](https://gitee.com/noear/solon-examples/tree/main/3.Solon-Web/demo3032-auth_sa_token)

* 基于 redisson 的应用


代码示例：

 [https://gitee.com/noear/solon-examples/tree/main/3.Solon-Web/demo3032-auth_sa_token_redisson](https://gitee.com/noear/solon-examples/tree/main/3.Solon-Web/demo3032-auth_sa_token_redisson)
 
 

* 更多参考

[《多种 Redis 接口适配复用一份配置》](/article/592)


### 5、借用 solon.auth 的注解与模板标签能力（如果用不到，则不引入；一般是用不到的）

添加桥接配置代码

```java
@Configuration
public class AuthBridgingConfig {
    @Bean
    public AuthAdapter initAuth(){
        return new AuthAdapter()
                .processor(new AuthProcessorImpl()) //设定认证处理器
                .failure((ctx, rst) -> { //设定默认的验证失败处理
                    ctx.render(rst);
                });
    }

    public static class AuthProcessorImpl implements AuthProcessor {
        @Override
        public boolean verifyIp(String ip) {
            return false;
        }

        @Override
        public boolean verifyLogined() {
            return StpUtil.isLogin();
        }

        @Override
        public boolean verifyPath(String path, String method) {
            return false;
        }

        @Override
        public boolean verifyPermissions(String[] permissions, Logical logical) {
            if (Logical.AND == logical) {
                return StpUtil.hasPermissionAnd(permissions);
            } else {
                return StpUtil.hasPermissionOr(permissions);
            }
        }

        @Override
        public boolean verifyRoles(String[] roles, Logical logical) {
            if (Logical.AND == logical) {
                return StpUtil.hasRoleAnd(roles);
            } else {
                return StpUtil.hasRoleOr(roles);
            }
        }
    }
}
```

使用 solon.auth 的后台模板标签能力（支持所有已适配模板）：

```xml
<@authPermissions name="user:del">
我有user:del权限
</@authPermissions>

<@authRoles name="admin">
我有admin角色
</@authRoles>
```



### 具体可参考

* [https://gitee.com/noear/solon-examples/tree/main/3.Solon-Web/demo3032-auth_sa_token](https://gitee.com/noear/solon-examples/tree/main/3.Solon-Web/demo3032-auth_sa_token)

* [https://gitee.com/noear/solon-examples/tree/main/3.Solon-Web/demo3032-auth_sa_token_redisson](https://gitee.com/noear/solon-examples/tree/main/3.Solon-Web/demo3032-auth_sa_token_redisson)
