SpringCloud+saToken实现登录及权限认证

2024-01-04

SpringCloud+saToken实现登录及权限认证

1.为什么要用sa-Token.

相比于我们常用的spring security框架,sa-Token更轻,更快,更优雅.使用过spring security的人肯定知道,spring security虽然灵活性很高,可以定制化各种复杂使用场景,但也因此导致框架很重,spring security使用的过滤器链每次请求认证也会花费大量性能,同时大部分功能都要自己手写完成.想比较之下,sa-Token更轻更快,开箱即用.比如登录认证时生成token的功能,spring security需要借助jwt写大量代码,而sa-Token只需要 StpUtil.login(Object id); 一个方法调用即可. 在 Sa-Token 中,大多数功能都可以一行代码解决

2. sa-Token功能

sa-Token可以满足我们大部分常规使用功能,可以解决: 登录认证 权限认证 单点登录 OAuth2.0 分布式Session会话 微服务网关鉴权 等一系列权限相关问题.

sa-Token权限认证实现方式和日常通用的设计思路,实现方式基本一致,只不过开箱即用,使用方便

在这里插入图片描述

3.springcloud集成sa-token

3.1 新建springcloud项目

我们需要至少有auth和gateway模块,auth作为用户登录认证模块,gateway里面需要实现网关统一鉴权功能,项目结构如下

在这里插入图片描述

3.2 auth模块功能实现

auth目录结构如下:

在这里插入图片描述

AuthController 代码:

@RestController
@AllArgsConstructor
@RequestMapping("/auth/")
//@Api(value = "用户授权认证", tags = "授权接口")
public class AuthController {
    @PostMapping("token")
    public R<AuthInfo> doLogin(@ApiParam(value = "授权类型", required = true) @RequestParam(defaultValue = "PASSWORD", required = false) GrantType grantType,
                               @ApiParam(value = "租户ID", required = true) @RequestParam(defaultValue = "000000", required = false) String tenantId,
                               @ApiParam(value = "账号") @RequestParam(required = false) String account,
                               @ApiParam(value = "密码") @RequestParam(required = false) String password) {

        UserParameter userParameter = UserParameter.builder()
                .tenantId(tenantId)
                .account(account)
                .password(password)
                .grantType(grantType)
                .build();

        //策略模式,兼容不同的登陆方式
        IUserInfoGranter userInfoGranter = UserInfoGranterBuilder.getUserInfoGranter(grantType);
        UserInfo userInfo = userInfoGranter.grant(userParameter);
        if (userInfo == null || userInfo.getUser() == null || userInfo.getUser().getId() == null) {
            return R.fail("用户名或密码错误");
        }

        return R.data(TokenGranter.createAuthInfo(userInfo));
    }

    // 查询登录状态,浏览器访问: http://localhost:8081/user/isLogin
    @RequestMapping("isLogin")
    public String isLogin() {
        return "当前会话是否登录:" + StpUtil.isLogin();
    }
}

IUserInfoGranter

public interface IUserInfoGranter {
    UserInfo grant(UserParameter userParameter);
}

PasswordUserInfoGranter (实际应用中密码需要加密处理)

@Component
@AllArgsConstructor
public class PasswordUserInfoGranter implements IUserInfoGranter {

    @Override
    public UserInfo grant(UserParameter userParameter) {
//        String tenantId = userParameter.getTenantId();
        String account = userParameter.getAccount();
        String password = userParameter.getPassword();

        // 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对
        if ("admin".equals(account) && "admin".equals(password)) {
            StpUtil.login(66666);
            // 第2步,获取 Token  相关参数
            SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
            return UserInfo.builder()
                    .user((User.builder().id(10001L).build()))
                    .roles(Lists.newArrayList("1", "2", "3"))
                    .build();
        }
        return null;
    }
}

PhoneNumUserInfoGranter

@Component
@AllArgsConstructor
public class PhoneNumUserInfoGranter implements IUserInfoGranter {

    @Override
    public UserInfo grant(UserParameter userParameter) {
        //TODO
        return null;
    }
}

TokenGranter

public class TokenGranter {

    /**
     * 创建认证token
     *
     * @param userInfo 用户信息
     * @return token
     */
    public static AuthInfo createAuthInfo(UserInfo userInfo) {
        User user = userInfo.getUser();
        
        StpUtil.login(user.getId());
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        
        AuthInfo authInfo = new AuthInfo();
        authInfo.setUserId(user.getId());
        authInfo.setTenantId(StringUtils.toStr(user.getTenantId()));
        authInfo.setOauthId(userInfo.getOauthId());
        authInfo.setAccount(user.getAccount());
        authInfo.setUserName(user.getRealName());
        authInfo.setAuthority(userInfo.getRoles().toString().substring(1, userInfo.getRoles().toString().length() - 1));
        authInfo.setAccessToken(tokenInfo.getTokenValue());
        authInfo.setTokenType("satoken");

        return authInfo;
    }

}

UserInfoGranterBuilder

@AllArgsConstructor
public class UserInfoGranterBuilder {
    private static final Map<GrantType, IUserInfoGranter> GRANTER_POOL = new HashMap<GrantType, IUserInfoGranter>();

    static {
        GRANTER_POOL.put(GrantType.PASSWORD, ApplicationContextHolder.getContext().getBean(PasswordUserInfoGranter.class));
        GRANTER_POOL.put(GrantType.PHONENUM, ApplicationContextHolder.getContext().getBean(PhoneNumUserInfoGranter.class));
    }

    public static IUserInfoGranter getUserInfoGranter(GrantType grantType) {
        IUserInfoGranter userInfoGranter = GRANTER_POOL.get(grantType);
        if (userInfoGranter == null) {
            throw new MSException("no grantType was found");
        } else {
            return userInfoGranter;
        }
    }
}

AuthInfo

@Data
public class AuthInfo {
    @ApiModelProperty("令牌")
    private String accessToken;
    @ApiModelProperty("令牌类型")
    private String tokenType;
    @ApiModelProperty("用户ID")
    @JsonSerialize(
            using = ToStringSerializer.class
    )
    private Long userId;
    @ApiModelProperty("租户ID")
    private String tenantId;
    @ApiModelProperty("第三方系统ID")
    private String oauthId;
    @ApiModelProperty("头像")
    private String avatar = "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png";
    @ApiModelProperty("角色名")
    private String authority;
    @ApiModelProperty("用户名")
    private String userName;
    @ApiModelProperty("账号名")
    private String account;
}

User

@Data
@Builder
public class User {

    private static final long serialVersionUID = 1L;

    /**
     * 主键id
     */
    @ApiModelProperty(value = "主键")
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;

    private Long tenantId;
    /**
     * 编号
     */
    private String code;
    /**
     * 账号
     */
    private String account;
    /**
     * 密码
     */
    private String password;
    /**
     * 昵称
     */
    private String name;
    /**
     * 真名
     */
    private String realName;
    /**
     * 头像
     */
    private String avatar;
    /**
     * 邮箱
     */
    private String email;
    /**
     * 手机
     */
    private String phone;
    /**
     * 生日
     */
    private Date birthday;
    /**
     * 性别
     */
    private Integer sex;
    /**
     * 角色id
     */
    private String roleId;
    /**
     * 部门id
     */
    private String deptId;
    /**
     * 部门id
     */
    private String postId;

}

UserInfo

@Data
@Builder
public class UserInfo implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 用户基础信息
     */
    @ApiModelProperty(value = "用户")
    private User user;

    /**
     * 权限标识集合
     */
    @ApiModelProperty(value = "权限集合")
    private List<String> permissions;

    /**
     * 角色集合
     */
    @ApiModelProperty(value = "角色集合")
    private List<String> roles;

    /**
     * 第三方授权id
     */
    @ApiModelProperty(value = "第三方授权id")
    private String oauthId;
}

UserParameter

@Data
@Builder
public class UserParameter {
    private String account;
    private String password;
    /**
     * 租户ID
     */
    private String tenantId;
    /**
     * 授权类型
     */
    private GrantType grantType;
    /**
     * 刷新令牌
     */
    private String refreshToken;
}

application.yml

server:
  # 端口
  port: 8100

############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
sa-token:
  # token 名称(同时也是 cookie 名称)
  token-name: satoken
  # token 有效期(单位:秒) 默认30天,-1 代表永久有效
  timeout: 2592000
  # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
  active-timeout: -1
  # 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
  is-concurrent: true
  # 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
  is-share: true
  # token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
  token-style: uuid
  # 是否输出操作日志
  is-log: true

#数据源配置
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://60.204.187.101:3306/blade?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8
    username: hetu2023
    password: R9YGxWEl1m
  data:
    redis:
      ##redis 单机环境配置
      host: 60.204.187.101
      port: 3379
      password:
      database: 0
      ssl:
        enabled: false
      ##redis 集群环境配置
      #cluster:
      #  nodes: 127.0.0.1:7001,127.0.0.1:7002,127.0.0.1:7003
      #  commandTimeout: 5000
      connect-timeout: 10s
      lettuce:
        pool:
          # 连接池最大连接数
          max-active: 200
          # 连接池最大阻塞等待时间(使用负值表示没有限制)
          max-wait: -1ms
          # 连接池中的最大空闲连接
          max-idle: 10
          # 连接池中的最小空闲连接
          min-idle: 0

pom 添加以下依赖, 这里注意,我是用的时3版本的spring,如果使用的3以下的,把***boot3改成boot就好

        <!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-spring-boot3-starter</artifactId>
            <version>1.37.0</version>
        </dependency>
        <!-- 需要引入Redis集成包,因为我们的网关和子服务主要通过Redis来同步数据  -->
        <!-- 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>

3.3 gateway模块功能实现

gateway目录结构如下:

在这里插入图片描述

SaTokenConfigure

@Configuration
public class SaTokenConfigure {
    // 注册 Sa-Token全局过滤器
    @Bean
    public SaReactorFilter getSaReactorFilter() {
        return new SaReactorFilter()
                // 拦截地址
                .addInclude("/**")    /* 拦截全部path */
                // 开放地址
                .addExclude("/favicon.ico")
                // 鉴权方法:每次访问进入
                .setAuth(obj -> {
                    // 登录校验 -- 拦截所有路由,并排除/user/doLogin 用于开放登录
                    SaRouter.match("/**", "/user/doLogin", r -> StpUtil.checkLogin());

                    // 权限认证 -- 不同模块, 校验不同权限
                    SaRouter.match("/user/**", r -> StpUtil.checkPermission("user"));
                    SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
                    SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods"));
                    SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders"));

                    // 更多匹配 ...  */
                })
                // 异常处理方法:每次setAuth函数出现异常时进入
                .setError(e -> {
                    return SaResult.error(e.getMessage());
                });
    }
}

StpInterfaceImpl

@Component  // 保证此类被 SpringBoot 扫描,完成 Sa-Token 的自定义权限验证扩展
public class StpInterfaceImpl implements StpInterface {

    /**
     * 返回一个账号所拥有的权限码集合
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        // 本 list 仅做模拟,实际项目中要根据具体业务逻辑来查询权限
        List<String> list = new ArrayList<String>();
        list.add("101");
        list.add("user.add");
        list.add("user.update");
        list.add("user.get");
        // list.add("user.delete");
        list.add("art.*");
        return list;
    }

    /**
     * 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        // 本 list 仅做模拟,实际项目中要根据具体业务逻辑来查询角色
        List<String> list = new ArrayList<String>();
        list.add("admin");
        list.add("super-admin");
        return list;
    }
}

application.yml

server:
  port: 8080

spring:
  application:
    name: cloud-gateway-service
  cloud:
    gateway:
      routes:
        #访问http://localhost:8080/guonei就可以转发到http://news.baidu.com/guonei
        - id: payment_routes2
          uri: http://news.baidu.com/
          predicates:
            - Path=/guonei/**

        - id: auth
          uri: http://localhost:8100
          predicates:
            - Path=/auth/**
      #开启自动定位功能 结合nacos注册中心使用
#      discovery:
#        locator:
#          enabled: true
  data:
    redis:
      ##redis 单机环境配置
      host: 60.204.187.101
      port: 3379
      password:
      database: 0
      ssl:
        enabled: false
      ##redis 集群环境配置
      #cluster:
      #  nodes: 127.0.0.1:7001,127.0.0.1:7002,127.0.0.1:7003
      #  commandTimeout: 5000
      connect-timeout: 10s
      lettuce:
        pool:
          # 连接池最大连接数
          max-active: 200
          # 连接池最大阻塞等待时间(使用负值表示没有限制)
          max-wait: -1ms
          # 连接池中的最大空闲连接
          max-idle: 10
          # 连接池中的最小空闲连接
          min-idle: 0

pom 中加入以下依赖, 注意springcloud gateway使用的是 Reactor 模型框架 ,需要引得依赖和mvc的不一样

        <!-- Sa-Token 权限认证(Reactor响应式集成),在线文档:https://sa-token.cc -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-reactor-spring-boot3-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>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

3.4 测试

输入正确账号密码后:

在这里插入图片描述

请求无权限的路由后:

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SpringCloud+saToken实现登录及权限认证 的相关文章

随机推荐

  • 860.染色法判定二分图

    二分图是指一个图中的所有顶点可以分为两部分 并且每条边连接的是属于不同部分的两个顶点 include
  • 进程 线程

    线程和进程是计算机科学中两个重要的概念 它们在多任务处理和并发执行中起着关键作用 进程 进程是程序执行时的一个实例 是程序执行到某种程度的数据结构的汇集 进程是资源分配的最小单位 拥有 资源所有权 和 调度执行 两个特性部分 进程能够分配给
  • 免费音效素材网站,一次性介绍清楚

    不管是在游戏 电影 电视剧 短视频还是音频中 合适的音效能够更好的表达内容和渲染氛围 今天给大家分享几个免费音效素材 感兴趣的话可以接着往下看 一 制片帮素材 找音效 制片帮素材不仅有海量的优质视频素材 还有丰富的音效资源 分类清晰 更重要
  • 【MySQL用户管理】

    目录 前言 用户管理 创建用户 删除用户 修改用户密码 修改用户密码安全检测设置 用户权限 添加权限 回收权限 总结 前言
  • 电动车低速提示音系统(AVAS)

    随着电动汽车的迅速发展 以及电动汽车的保有量也越来越多 根据车辆的特征来说电动汽车相比于传统的内燃机汽车要安静 为了保护行人 减少事故的发生 欧盟最近发布了一项关于电动车的新法规 自2019年7月1日开始 欧盟关于电动汽车的最新法律正式生效
  • Win7系统提示找不到KBDUK.DLL文件的解决办法

    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题 如果是新手第一时间会认为是软件或游戏出错了 其实并不是这样 其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库 这时你可以下载这个KBDUK D
  • [每周一更]-(第55期):Go的interface

    参考地址 https juejin cn post 6978322067775029261 https gobyexample com interfaces https go dev tour methods 9 介绍下Go的interfa
  • CSSTree:CSS解析与转换的强大工具集

    CSS作为前端开发中不可或缺的一部分 负责网页的样式和布局 但处理CSS的复杂性常常让开发者感到头疼 为了解决这个问题 CSSTree应运而生 CSSTree是一个基于规范和浏览器实现的工具集 旨在提供快速 详细的CSS解析器 CSS AS
  • 学习使用layPage, 多功能JS分页组件/插件的方法

    学习使用layPage 多功能JS分页组件 插件的方法 效果图 分页代码 效果图 点击查看链接 分页代码
  • 深入理解左倾红黑树 | 京东物流技术团队

    平衡二叉搜索树 平衡二叉搜索树 Balanced Binary Search Tree 的每个节点的左右子树高度差不超过 1 它可以在 O logn 时间复杂度内完成插入 查找和删除操作 最早被提出的自平衡二叉搜索树是 AVL 树 AVL
  • Win7系统提示找不到KBDURDU.DLL文件的解决办法

    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题 如果是新手第一时间会认为是软件或游戏出错了 其实并不是这样 其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库 这时你可以下载这个KBDURDU
  • 如何使用Requests库采集前程无忧招聘数据

    使用Requests库来采集前程无忧 智联招聘 的数据涉及以下步骤 了解目标网站结构 首先 需要了解前程无忧网站的结构 查看其页面布局 URL结构和需要采集的信息位置 发送HTTP请求 使用Requests库发送HTTP请求获取页面内容 通
  • 使用pytorch构建图卷积网络预测化学分子性质

    在本文中 我们将通过化学的视角探索图卷积网络 我们将尝试将网络的特征与自然科学中的传统模型进行比较 并思考为什么它的工作效果要比传统的方法好 图和图神经网络 化学或物理中的模型通常是一个连续函数 例如y f x x x x 其中x x x
  • CAD Exchanger SDK 3.24 for Android Crack

    CAD Exchanger SDK Software Libraries to Read Write and Visualize 3D CAD files Quickly and easily enrich your web server
  • 蜜罐溯源以及蜜罐HFish的使用

    一 蜜罐是什么 蜜罐技术本质上是一种对攻击方进行欺骗的技术 通过布置一些作为诱饵的主机 网络服务或者信息 诱使攻击方对它们实施攻击 从而可以对攻击行为进行捕获和分析 了解攻击方所使用的工具与方法 推测攻击意图和动机 能够让防御方清晰地了解他
  • Matlab图像处理系列——图像复原之噪声模型仿真

    微信公众号上线 搜索公众号 小灰灰的FPGA 关注可获取相关源码 定期更新有关FPGA的项目以及开源项目源码 包括但不限于各类检测芯片驱动 低速接口驱动 高速接口驱动 数据信号处理 图像处理以及AXI总线等 本节目录 一 图像复原的模型 二
  • VS Code 自动选择Python3 venv

    我们使用VS Code写Python代码时 往往希望这个项目的依赖和其他项目或者全局的python环境隔离开 VS Code不像PyCharm那样自动完成 但是我们也可以快速的进行设置 首先我们需要把python项目所在的目录添加为VS C
  • CAD Exchanger SDK 3.24 FOR WIN Crack

    Manufacturing Toolkit and Web Toolkit enhancements Unity performance optimization renaming and rotating SDK examples in
  • vscode插件离线安装地址

    因内网开发 编辑器不可联网 插件需要离线安装 vscode插件商店 Extensions for Visual Studio family of products Visual Studio Marketplace
  • SpringCloud+saToken实现登录及权限认证

    SpringCloud saToken实现登录及权限认证 文章目录 SpringCloud saToken实现登录及权限认证 1 为什么要用sa Token 2 sa Token功能 3 springcloud集成sa token 3 1