Java小强个人技术博客站点    手机版
当前位置: 首页 >> 开源 >> 权限框架Sa-Token

权限框架Sa-Token

15000 开源 | 2023-3-14

一个轻量级 java 权限认证框架,让鉴权变得简单、优雅!

https://sa-token.cc/index.html 

sa-token.jpg

Sa-Token 目前主要五大功能模块:登录认证、权限认证、单点登录、OAuth2.0、微服务鉴权。

登录认证 —— 单端登录、多端登录、同端互斥登录、七天内免登录

权限认证 —— 权限认证、角色认证、会话二级认证

Session会话 —— 全端共享Session、单端独享Session、自定义Session

踢人下线 —— 根据账号id踢人下线、根据Token值踢人下线

账号封禁 —— 登录封禁、按照业务分类封禁、按照处罚阶梯封禁

持久层扩展 —— 可集成Redis、Memcached等专业缓存中间件,重启数据不丢失

分布式会话 —— 提供jwt集成、共享数据中心两种分布式会话方案

微服务网关鉴权 —— 适配Gateway、ShenYu、Zuul等常见网关的路由拦截认证

单点登录 —— 内置三种单点登录模式:无论是否跨域、是否共享Redis,都可以搞定

OAuth2.0认证 —— 轻松搭建 OAuth2.0 服务,支持openid模式

二级认证 —— 在已登录的基础上再次认证,保证安全性

Basic认证 —— 一行代码接入 Http Basic 认证

独立Redis —— 将权限缓存与业务缓存分离

临时Token认证 —— 解决短时间的Token授权问题

模拟他人账号 —— 实时操作任意用户状态数据

临时身份切换 —— 将会话身份临时切换为其它账号

前后台分离 —— APP、小程序等不支持Cookie的终端

同端互斥登录 —— 像QQ一样手机电脑同时在线,但是两个手机上互斥登录

多账号认证体系 —— 比如一个商城项目的user表和admin表分开鉴权

Token风格定制 —— 内置六种Token风格,还可:自定义Token生成策略、自定义Token前缀

注解式鉴权 —— 优雅的将鉴权与业务代码分离

路由拦截式鉴权 —— 根据路由拦截鉴权,可适配restful模式

自动续签 —— 提供两种Token过期策略,灵活搭配使用,还可自动续签

会话治理 —— 提供方便灵活的会话查询接口

记住我模式 —— 适配[记住我]模式,重启浏览器免验证

密码加密 —— 提供密码加密模块,可快速MD5、SHA1、SHA256、AES、RSA加密

全局侦听器 —— 在用户登陆、注销、被踢下线等关键性操作时进行一些AOP操作

开箱即用 —— 提供SpringMVC、WebFlux等常见web框架starter集成包,真正的开箱即用


根据文档,我们来做示例

首先POM引入相关依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
    <dependency>
        <groupId>cn.dev33</groupId>
        <artifactId>sa-token-spring-boot-starter</artifactId>
        <version>1.34.0</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.79</version>
    </dependency>
    <!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
    <dependency>
        <groupId>cn.dev33</groupId>
        <artifactId>sa-token-dao-redis-jackson</artifactId>
        <version>1.34.0</version>
    </dependency>
    <!-- 提供Redis连接池 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
</dependencies>



权限赋值接口,这里为了演示,用代码写死了用户的权限和角色

package com.example.springbootfast.config;
import cn.dev33.satoken.stp.StpInterface;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
 * 自定义权限验证接口扩展
 * 保证此类被SpringBoot扫描,完成Sa-Token的自定义权限验证扩展
 */
@Component
public class StpInterfaceImpl implements StpInterface {
    /**
     * 返回一个账号所拥有的权限码集合
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限
        List<String> list = new ArrayList<>();
        list.add("user.add");
        list.add("user.update");
        list.add("user.get");
        // Sa-Token允许你根据通配符指定泛权限,例如当一个账号拥有art.*的权限时,art.add、art.delete、art.update都将匹配通过
        // 当一个账号拥有 "*" 权限时,他可以验证通过任何权限码 (角色认证同理)
        list.add("art.*");
        return list;
    }
    /**
     * 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色
        List<String> list = new ArrayList<>();
        list.add("user");
        return list;
    }
}


为了演示注解实现权限验证,需要增加配置

package com.example.springbootfast.config;
import cn.dev33.satoken.interceptor.SaInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
    // 注册 Sa-Token 拦截器,打开注解式鉴权功能
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册 Sa-Token 拦截器,打开注解式鉴权功能
        registry.addInterceptor(new SaInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/user/doLogin");
    }
}


Spring配置文件

spring:
  application:
    name: springBootFast
  # redis配置
  redis:
    # Redis数据库索引(默认为0)
    database: 1
    # Redis服务器地址
    host: 127.0.0.1
    # Redis服务器连接端口
    port: 6379
    # Redis服务器连接密码(默认为空)
    password: l52u27lv1Jur
    # 连接超时时间
    timeout: 10s
    lettuce:
      pool:
        # 连接池最大连接数
        max-active: 200
        # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms
        # 连接池中的最大空闲连接
        max-idle: 10
        # 连接池中的最小空闲连接
        min-idle: 0
server:
  port: 8081
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
sa-token:
  # token名称 (同时也是cookie名称)
  token-name: satoken
  # token有效期,单位s 默认30天, -1代表永不过期
  timeout: 2592000
  # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
  activity-timeout: -1
  # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
  is-concurrent: true
  # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
  is-share: true
  # token风格
  token-style: uuid
  # 是否输出操作日志
  is-log: false


编写用于测试的Controller接口

package com.example.springbootfast.controller;
import cn.dev33.satoken.annotation.*;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.alibaba.fastjson.JSON;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user/")
public class UserController {
    // 测试登录,浏览器访问: http://localhost:8081/user/doLogin?username=javacui&password=123456
    @RequestMapping("doLogin")
    public SaResult doLogin(String username, String password) {
        // 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对
        if("javacui".equals(username) && "123456".equals(password)) {
            // 根据账号id,进行登录
            StpUtil.login(10001);
            SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
            System.out.println("登录信息:" + JSON.toJSONString(tokenInfo));
            return SaResult.data(tokenInfo);
        }
        return SaResult.error("登录失败");
    }
    // 查询登录状态,浏览器访问: http://localhost:8081/user/isLogin
    @RequestMapping("isLogin")
    public String isLogin() {
        // 检验当前会话是否已经登录, 如果未登录,则抛出异常:`NotLoginException`
        // StpUtil.checkLogin();
        // 获取当前会话账号id, 如果未登录,则抛出异常:`NotLoginException`
        System.out.println(StpUtil.getLoginId() + "-->" + StpUtil.getTokenValue());
        return "当前会话是否登录:" + StpUtil.isLogin();
    }
    // 当前会话注销登录,浏览器访问: http://localhost:8081/user/logout
    @RequestMapping("logout")
    public SaResult logout() {
        StpUtil.logout();
        return SaResult.ok("退出成功");
    }
    // 权限验证 http://localhost:8081/user/checkPermissionInCode
    @RequestMapping("checkPermissionInCode")
    public SaResult checkPermissionInCode() {
        // 获取:当前账号所拥有的权限集合
        System.out.println("权限集合:" + JSON.toJSONString(StpUtil.getPermissionList()));
        // 判断:当前账号是否含有指定权限, 返回 true 或 false
        System.out.println("user.add权限:" + StpUtil.hasPermission("user.add"));
        // 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
        try {
            StpUtil.checkPermission("user.add");
            System.out.println("user.add权限:True");
        }catch (Exception e){
            System.out.println("user.add权限:False");
        }
        // 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过]
        try {
            StpUtil.checkPermissionAnd("user.add", "user.delete", "user.get");
            System.out.println("多权限AND:True");
        } catch (Exception e){
            System.out.println("多权限AND:False");
        }
        // 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
        try {
            StpUtil.checkPermissionOr("user.add", "user.delete", "user.get");
            System.out.println("多权限OR:True");
        } catch (Exception e){
            System.out.println("多权限OR:False");
        }
        return SaResult.ok("验证成功");
    }
    // 登录校验:只有登录之后才能进入该方法 http://localhost:8081/user/info
    @SaCheckLogin
    @RequestMapping("info")
    public SaResult info() {
        return SaResult.ok("查询用户信息通过");
    }
    // 角色校验:必须具有指定角色才能进入该方法  http://localhost:8081/user/checkRole
    @SaCheckRole("admin")
    @RequestMapping("checkRole")
    public SaResult addForRole() {
        return SaResult.ok("用户有此角色");
    }
    // 权限校验:必须具有指定权限才能进入该方法  http://localhost:8081/user/checkPermission
    @SaCheckPermission(value = {"user.add", "user.update"}, mode = SaMode.AND)
    @RequestMapping("checkPermission")
    public SaResult checkPermission() {
        return SaResult.ok("用户有此权限");
    }
}


使用API-POST测试

访问:http://localhost:8081/user/doLogin?username=javacui&password=123456 

返回内容:

{
    "code": 200,
    "msg": "ok",
    "data": {
        "tokenName": "satoken",
        "tokenValue": "674a2a4c-3cc5-4a2c-8c3c-b4e6f268ecf6",
        "isLogin": true,
        "loginId": "10001",
        "loginType": "login",
        "tokenTimeout": 2592000,
        "sessionTimeout": 2592000,
        "tokenSessionTimeout": -2,
        "tokenActivityTimeout": -1,
        "loginDevice": "default-device",
        "tag": null
    }
}

然后在访问其他接口时,增加Header,satoken:674a2a4c-3cc5-4a2c-8c3c-b4e6f268ecf6,即可。



至于SA-TOKEN其他,比如:

二级认证

单点登录模块

OAuth2.0的应用

分布式Session会话

JWT集成

等等,因为这就是国人写的软件,文档非常齐全而且是中文,不管文档还是看源码,都能很快解决你的疑问。


END

推荐您阅读更多有关于“ 权限 JWT Token 认证框架 ”的文章

上一篇:Windows下如何查看端口被谁占用 下一篇:在线构建自动部署软件JPOM

猜你喜欢

发表评论: