Sa-Token 认证、鉴权框架

Sa-Token官网

Spring Boot + Redis 集成

<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-spring-boot-starter</artifactId>
    <version>1.37.0</version>
</dependency>

<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-redis-jackson</artifactId>
    <version>1.37.0</version>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
sa-token:
  # token 名称(同时也是 cookie 名称)
  token-name: satoken
  # token前缀
  token-prefix: Brear
  # token 有效期(单位:秒) 默认30天,-1 代表永久有效
  timeout: 2592000
  # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
  active-timeout: -1
  # 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
  is-concurrent: true
  # 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
  is-share: true
  # 同一账号最大登录数量,-1代表不限 (只有在 isConcurrent=true,isShare=false 时此配置才有效
  max-login-count: 2
  # token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
  token-style: random-32
  # 是否输出操作日志
  is-log: true
  # 是否打开自动续签,为true框架会在每次直接或间接调用getLoginId()时进行一次过期检查和续签操作
  autoRenew: true
  # 是否尝试从cookie中读取token 此值为false StpUtils.login()登录时也不会往前端注入cookie
  is-read-cookie: false
spring:
  # redis配置
  redis:
    # Redis数据库索引
    database: 1
    # Redis服务器地址
    host: 127.0.0.1
    # 密码
    password: 
    # Redis服务器连接端口
    port: 6379
    # Redis服务器连接密码(默认为空)
    # password:
    # 连接超时时间
    timeout: 10s
    lettuce:
      pool:
        # 连接池最大连接数
        max-active: 200
        # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1
        # 连接池中的最大空闲连接
        max-idle: 10
        # 连接池中的最小空闲连接
        min-idle: 0

StpUtil.login()
StpUtil.getTokenInfo()

拦截器方式

@Configuration
public class SaTokenConfigurer implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SaInterceptor(handler -> {
            // 登录认证
            SaRouter
            .match("/**") // 拦截的path列表,可以写多个
            .notMatch("/doLogin") // 放行的接口
            .check(r -> StpUtil.checkLogin());
            // 鉴权拦截
            SaRouter.match("/user/add", r -> StpUtil.checkPermission("user:add"))
            .match("/user/delete", r -> StpUtil.checkPermission("user:delete"))
            .match("/user/update", r -> StpUtil.checkPermission("user:update"));
        })).addPathPatterns("/**");
    }
}

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = NotLoginException.class)
    @ResponseStatus(code = HttpStatus.UNAUTHORIZED)
    public R notLoginException(NotLoginException e) {
        return R.fail("未登录");
    }

    @ExceptionHandler(value = NotPermissionException.class)
    @ResponseStatus(code = HttpStatus.FORBIDDEN)
    public R notPermissionException(NotPermissionException e) {
        return R.fail("无此权限");
    }
}

全局过滤器

@Configuration
@Slf4j
public class SaTokenFilterConfigurer {

    /**
     * @return {@link SaServletFilter}
     */
    @Bean
    public SaServletFilter getSaServletFilter() {
        return new SaServletFilter()
                // 拦截路由
                .addInclude("/**")
                // 过滤路由
                .addExclude("/favicon.ico")

                .setAuth(obj -> {
                    SaRouter.match("/**", "/doLogin", StpUtil::checkLogin);

                    SaRouter.match("/user/add", r -> StpUtil.checkPermission("user:add"));
                    SaRouter.match("/user/delete", r -> StpUtil.checkPermission("user:delete"));

                })
                // 认证、鉴权异常会来此处
                .setError(e -> {
                    log.error("认证/授权出错,错误信息:", e);
                    SaResponse response = SaHolder.getResponse();
                    SaHolder.getResponse().setHeader("Content-Type", "application/json;charset=UTF-8");
                    String errMsg = "请求失败";
                    if (e instanceof NotPermissionException) {
                        // 没有权限
                        response.setStatus(HttpStatus.FORBIDDEN.value());
                        errMsg = "无此权限";
                    } else if (e instanceof NotLoginException) {
                        // 未登录
                        response.setStatus(HttpStatus.UNAUTHORIZED.value());
                        errMsg = "未登录";
                    }
                    return JSONUtil.toJsonStr(R.fail(errMsg));
                })

                // 前置函数,在每次认证函数之前执行(不受includeList和excludeList的限制,所有的请求都会进入)
                .setBeforeAuth(r -> {
                    log.info("访问系统,信息:{}", r);
                    // 设置一些安全响应头
                    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");

                });

    }
}

StpInterface 鉴权数据的来源

/**
 * 在使用权限校验 API 之前,你必须实现此接口StpInterface,告诉框架哪些用户拥有哪些权限
 * 框架默认不对数据进行缓存,如果你的数据是从数据库中读取的,一般情况下你需要手动实现数据的缓存读写。
 **/
@Component
@Slf4j
public class StpInterfaceImpl implements StpInterface {

    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        List<String> permissionList = new ArrayList<>();
        
        return permissionList;
    }

    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        List<String> roleList = new ArrayList<>();
        
        return roleList;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值