Spring Boot + Redis + Sa-Token

news2025/1/7 11:53:14

参考文献

Sa-Token实现分布式登录鉴权(Redis集成 前后端分离)-腾讯云开发者社区-腾讯云

介绍

StpInterface 是 Sa-Token 框架中的一个接口,属于 Sa-Token 身份认证与授权框架的一部分。该接口提供了一些方法来实现自定义的身份认证和授权管理功能,特别是针对自定义的权限验证。

StpInterface类的主要功能

StpInterface 用于定义 Sa-Token 中与用户身份相关的核心操作接口。通过实现这个接口,用户可以自定义如何获取用户信息、验证用户身份、判断是否有权限等。

主要方法

StpInterface 主要包括以下几个常用的方法:

  1. getLoginId():获取当前登录用户的唯一标识(例如用户 ID)。

    String getLoginId();

  2. isLogin():判断当前是否已登录。

    boolean isLogin();

  3. login(Object loginId):登录方法,传入一个唯一标识来进行用户登录。

    void login(Object loginId);

  4. logout():登出方法,清除用户的登录状态。

    void logout();

  5. hasPermission(String permission):判断当前登录用户是否具有某个权限。

    boolean hasPermission(String permission);

  6. hasRole(String role):判断当前登录用户是否拥有某个角色。

    boolean hasRole(String role);

使用场景

  • 自定义身份认证:如果需要自定义登录逻辑或用户身份验证,可以实现 StpInterface 接口来替代 Sa-Token 默认的用户认证方式。
  • 角色与权限管理:通过 hasRolehasPermission 等方法,进行角色与权限的验证,保证应用中的授权机制符合业务需求。

示例

以下是一个简单的实现例子,展示了如何实现 StpInterface 接口来定制认证与授权逻辑:

import cn.dev33.satoken.stp.StpInterface;
import org.springframework.stereotype.Component;

@Component
public class MyStpInterface implements StpInterface {

    @Override
    public String getLoginId() {
        // 返回当前登录用户的ID
        return "123"; // 假设返回用户ID为123
    }

    @Override
    public boolean isLogin() {
        // 判断当前用户是否登录
        return true; // 假设用户已登录
    }

    @Override
    public void login(Object loginId) {
        // 实现用户登录逻辑
        // 这里可以根据传入的loginId来设置用户的登录状态
    }

    @Override
    public void logout() {
        // 实现登出逻辑
        // 清除用户的登录状态
    }

    @Override
    public boolean hasPermission(String permission) {
        // 判断用户是否有某个权限
        return "admin".equals(permission); // 假设只有管理员有权限
    }

    @Override
    public boolean hasRole(String role) {
        // 判断用户是否有某个角色
        return "admin".equals(role); // 假设只有管理员有该角色
    }
}

通过实现 StpInterface,你可以根据实际业务需求来定制用户认证、登录状态、权限验证等操作。

基于原有的基础 | 创建MySQL表单存储权限

-- 角色表
CREATE TABLE roles (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    role_name VARCHAR(50) NOT NULL
);

-- 权限表
CREATE TABLE permissions (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    permission_name VARCHAR(50) NOT NULL
);

-- 用户角色关联表
CREATE TABLE users_roles (
    user_id BIGINT NOT NULL,
    role_id BIGINT NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(id),
    FOREIGN KEY (role_id) REFERENCES roles(id)
);

-- 角色权限关联表
CREATE TABLE roles_permissions (
    role_id BIGINT NOT NULL,
    permission_id BIGINT NOT NULL,
    FOREIGN KEY (role_id) REFERENCES roles(id),
    FOREIGN KEY (permission_id) REFERENCES permissions(id)
);

-- 用户表
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,        -- 用户唯一标识(主键)
    username VARCHAR(50) NOT NULL UNIQUE,       -- 用户名(唯一)
    password VARCHAR(255) NOT NULL,             -- 密码(加密存储)
    email VARCHAR(100) DEFAULT NULL,            -- 邮箱(可选)
    phone VARCHAR(20) DEFAULT NULL,             -- 手机号(可选)
    status TINYINT DEFAULT 1,                   -- 用户状态(1=启用, 0=禁用)
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间
    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- 更新时间
    last_login_time TIMESTAMP NULL DEFAULT NULL -- 上次登录时间
);

INSERT INTO users (username, password, email, phone, status, create_time, last_login_time) VALUES
('zhangsan', '$2a$10$eC9yWZaMjMEbfBOAAsXHg.SUz3aHtYZJ/riMjHJ.TOu3NHsMFTm.a', 'zhangsan@example.com', '1234567890', 1, NOW(), NULL),
('lisi', '$2a$10$txB4zY7lqr9Kx.XHcGB5ruMiOBpFMHLF9rljN5iGtZ1o26g/.Agxe', 'lisi@example.com', '0987654321', 1, NOW(), NULL);

INSERT INTO roles (id, role_name) VALUES
(1, 'ADMIN'),
(2, 'USER');

INSERT INTO permissions (id, permission_name) VALUES
(1, 'user.add'),
(2, 'user.delete'),
(3, 'user.update'),
(4, 'user.view');

INSERT INTO users_roles (user_id, role_id) VALUES
(1, 1), -- zhangsan -> ADMIN
(2, 2); -- lisi -> USER

INSERT INTO roles_permissions (role_id, permission_id) VALUES
(1, 1), -- ADMIN 拥有 user.add 权限
(1, 2), -- ADMIN 拥有 user.delete 权限
(1, 3), -- ADMIN 拥有 user.update 权限
(1, 4), -- ADMIN 拥有 user.view 权限
(2, 4); -- USER 只拥有 user.view 权限

注意:密码已经使用 bcrypt 加密,明文分别为:

  • zhangsan: password123
  • lisi: mypassword

UserController

package com.example.satokendemo.controller;  
  
import cn.dev33.satoken.stp.SaTokenInfo;  
import cn.dev33.satoken.stp.StpUtil;  
import cn.dev33.satoken.util.SaResult;  
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;  
import com.example.satokendemo.mapper.UserMapper;  
import com.example.satokendemo.mapper.PermissionMapper;  
import com.example.satokendemo.pojo.User;  
import com.example.satokendemo.util.PasswordUtil;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.RestController;  
  
import java.util.List;  
  
@RestController  
@RequestMapping("/user")  
public class UserController {  
  
    @Autowired  
    private UserMapper userMapper;  
  
    @Autowired  
    private PermissionMapper permissionMapper;  
  
    /**  
     * 用户登录  
     */  
    @RequestMapping("/doLogin")  
    public SaResult doLogin(String username, String password) {  
        // 第1步:从数据库查询用户信息  
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();  
        queryWrapper.eq("username", username);  
        User user = userMapper.selectOne(queryWrapper);  // 查询用户  
  
        if (user == null) {  
            System.out.println("用户不存在");  
        } else {  
            System.out.println("找到用户: " + user.getUsername());  
        }  
  
        if (!PasswordUtil.verify(password, user.getPassword())) {  
            System.out.println("用户输入密码: " + password);  
            System.out.println("数据库存储的加密密码: " + user.getPassword());  
            System.out.println("密码校验失败");  
        }  
  
        // 如果用户不存在或者密码不匹配,返回登录失败  
        if (user == null || !PasswordUtil.verify(password, user.getPassword())) { // 使用加密策略校验密码  
            return SaResult.error("用户名或密码错误");  
        }  
  
        // 第2步:登录  
        StpUtil.login(user.getId());  
  
        // 第3步:加载用户信息和权限信息  
        StpUtil.getSession().set("loginInfo", user);  
  
        // 加载用户权限  
        List<String> authList = permissionMapper.getPermissionsByUserId(user.getId());  
        StpUtil.getSession().set("authList", authList);  
  
        // 第4步:获取 Token 相关参数  
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();  
  
        // 第5步:返回给前端  
        return SaResult.data(tokenInfo);  
    }  
  
    /**  
     * 查询登录状态  
     */  
    @RequestMapping("/isLogin")  
    public String isLogin() {  
        return "当前会话是否登录:" + StpUtil.isLogin();  
    }  
  
    /**  
     * 获取当前登录用户信息  
     */  
    @RequestMapping("/getUserInfo")  
    public User getUserInfo() {  
        return (User) StpUtil.getSession().get("loginInfo");  
    }  
  
    /**  
     * 测试方法:校验权限 - 添加操作  
     */  
    @GetMapping("/add")  
    public String add() {  
        StpUtil.checkPermission("user.add");  
        return "ok";  
    }  
  
    /**  
     * 测试方法:校验权限 - 更新操作  
     */  
    @GetMapping("/update")  
    public String update() {  
        StpUtil.checkPermission("user.update");  
        return "ok";  
    }  
}

这里我把controller中的模拟数据改成了mysql数据库的数据并将密码进行加密,注册时也可以通过相应的方法对明文密码进行加密处理后存储至数据库中,使得数据更加安全。

完成后的图例应该如下图所示:

数据库的用户表单(角色,权限,具体信息,绑定关系…)
在这里插入图片描述

在原本代码的基础上和MySQL连接并完成权限认证与登录

其它的表单的实体类都可以直接使用mybatis-plus去完成增删改查,但permissions表单的实体类得在mapper中添加一个方法使用。

PermissionMapper
完成对权限的使用

public interface PermissionMapper extends BaseMapper<Permission> {  
    // 获取用户的所有权限名称  
    @Select("SELECT p.permission_name " +  
            "FROM permissions p " +  
            "JOIN roles_permissions rp ON p.id = rp.permission_id " +  
            "JOIN users_roles ur ON rp.role_id = ur.role_id " +  
            "WHERE ur.user_id = #{userId}")  
    List<String> getPermissionsByUserId(@Param("userId") Long userId);  
}

之后是我yml的配置

server:  
  # 端口  
  port: 8081  
  
spring:  
  datasource:  
    username: root  
    password: 20050101  
    url: jdbc:mysql://localhost:3306/sa_token?serverTimezone=UTC&userUnicode=true&characterEncoding=utf-8  
    driver-class-name: com.mysql.cj.jdbc.Driver  
  # redis配置  
  redis:  
    # Redis数据库索引(默认为0)  
    database: 0  
    # Redis服务器地址  
    host: 127.0.0.1  
    # Redis服务器连接端口  
    port: 6379  
    # Redis服务器连接密码(默认为空)  
    # password:  
    # 连接超时时间  
    timeout: 10s  
    lettuce:  
      pool:  
        # 连接池最大连接数  
        max-active: 200  
        # 连接池最大阻塞等待时间(使用负值表示没有限制)  
        max-wait: -1ms  
        # 连接池中的最大空闲连接  
        max-idle: 10  
        # 连接池中的最小空闲连接  
        min-idle: 0  
  jpa:  
    open-in-view: false  
  
mybatis:  
  mapper-locations: classpath:mapper/*.xml  
  type-aliases-package: com.example.satokendemo.pojo  
############## 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

在这里插入图片描述

这是spring boot项目的大体框架,可以按照我这个来。

之后启动项目
通过api工具postman进行登录测试,例如:http://localhost:8082/user/doLogin?username=lisi&password=mypassword

/**  
 * 示例代码:生成加密密码(可用于初始化数据库)  
 */  
public static void main(String[] args) {  
    String userPassword = "password123"; // 明文密码  
    String storedPassword = "$2a$10$eC9yWZaMjMEbfBOAAsXHg.SUz3aHtYZJ/riMjHJ.TOu3NHsMFTm.a"; // 从数据库获取的加密密码  
    boolean isValid = PasswordUtil.verify(userPassword, storedPassword);  
    System.out.println("密码验证结果: " + isValid);  
    String encryptedPassword = PasswordUtil.encrypt(userPassword);  
    System.out.println("加密后的密码: " + encryptedPassword);  
    System.out.println("加密后的密码与数据库中的密码是否匹配: " + PasswordUtil.verify(userPassword, encryptedPassword  
    )); // 再次验证加密后的密码  
}

记得用这个工具类去改一下数据库的加密密码

使用postman测试后可以得到以下信息

在这里插入图片描述

在redis中也可以看到我们的数据已经传递成功了
在这里插入图片描述

无论是权限还是相关的用户信息都是已经成功传到redis缓存了,之后就是携带token去测试接口调用看是否符合我们的权限。

不携带token去访问接口 http://localhost:8081/user/add

在这里插入图片描述

携带token访问

在这里插入图片描述

没有相应的权限的用户携带token去访问

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2271597.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智慧工地信息管理与智能预警平台

建设背景与政策导向 智慧工地信息管理与智能预警平台的出现&#xff0c;源于工地管理面临的诸多挑战&#xff0c;如施工地点分散、危险区域多、监控手段落后等。随着政府对建筑产业现代化的积极推动&#xff0c;各地纷纷出台政策支持智慧工地的发展&#xff0c;旨在通过信息技…

GoF23种设计模式 简介

文章目录 面向对象(OO)设计原则&#xff08;7&#xff09;单一职责原则开闭原则里氏代换原则依赖倒转原则接口隔离原则合成复用原则迪米特法则 创建型模式 &#xff08;5&#xff09;工厂方法模式 &#xff08;类模式&#xff0c;其余都是对象模式&#xff09;抽象工厂模式建造…

文献阅读 | B. S. Carmo 2010

目录 一、文献名称二、原文地址三、ABSTRACT研究方法主要发现结论 四、INTRODUCTION研究背景涡旋脱落与脱落模式脱落模式分类SG&#xff08;间隙对称脱落&#xff09;AG&#xff08;间隙交替脱落&#xff09;WG&#xff08;间隙尾流脱落&#xff09; 拖力反转 相关研究以前的研…

机器学习之过采样和下采样调整不均衡样本的逻辑回归模型

过采样和下采样调整不均衡样本的逻辑回归模型 目录 过采样和下采样调整不均衡样本的逻辑回归模型1 过采样1.1 样本不均衡1.2 概念1.3 图片理解1.4 SMOTE算法1.5 算法导入1.6 函数及格式1.7 样本类别可视化理解 2 下采样2.1 概念2.2 图片理解2.3 数据处理理解2.4 样本类别可视化…

unity学习7:unity的3D项目的基本操作: 坐标系

目录 学习参考 1 unity的坐标系 1.1 左手坐标系 1.2 左手坐标系和右手坐标系的区别 1.3 坐标系的原点(0,0,0) 2 坐标系下的具体xyz坐标 2.1 position这里的具体xyz坐标值 2.2 父坐标 2.3 世界坐标和相对坐标 2.3.1 世界坐标 2.3.2 相对坐标 2.4 父物体&#xff0c;…

【读书笔记·VLSI电路设计方法解密】问题36:一个好的设计流程有哪些特点

由于IC实现与不断演进的技术节点密切相关,且各种新问题迅速涌现,一个优秀的设计流程必须具备灵活性,以应对这些新挑战,而无需进行大规模调整。 与此同时,为了克服当今SoC实现领域中出现的众多问题,整个EDA行业正在高速运转。新工具正在加速涌现;因此,一个优秀的设计流…

【读书笔记·VLSI电路设计方法解密】问题35:ASIC设计流程的两个主要方面是什么

毫无疑问,ASIC设计流程是一个复杂的系统,包含了许多商业CAD工具以及许多内部开发的工具或脚本。然而,无论流程中集成了多少工具或脚本,ASIC设计流程的核心目标始终可以归结为两个关键点:创建和检查。 创建过程指的是生成硬件的活动,例如RTL编码、逻辑综合以及布局布线。…

Linux上安装配置单节点zookeeper

直接先去官网下载安装包&#xff0c; https://downloads.apache.org/zookeeper/ 选择合适的版本&#xff0c;然后上传至服务器 解压&#xff1a; tar -zxvf apache-zookeeper-3.9.3-bin.tar.gz创建data和logs目录 mkdir data mkdir logs配置环境变量&#xff1a; vim /etc/p…

ModuleNotFoundError: No module named XXX

我们在安装了某个包之后&#xff0c;还是提示找不到包 方法一&#xff1a; python -m pip install 包名 -i https://pypi.tuna.tsinghua.edu.cn/simple 方法二&#xff1a; conda install 包名 如果还是找不到包&#xff1a; 请检查环境&#xff1a;

(leetcode算法题)384. 打乱数组 398. 随机数索引

问题转化&#xff1a; 题目要求将nums中的数字出现的次序随机打乱 转化成&#xff1a;对于 0 号位置来说&#xff0c;nums[i], ..., nums[n - 1] 可以等概率的出现 ... && ... && 对于 n - 1号位置来说&#xff0c;nums[i], ..., nums[n - 1] 可以等概率的出…

从零开始开发纯血鸿蒙应用之实现起始页

从零开始开发纯血鸿蒙应用 一、前言二、主要页面三、应用起始页四、MainPageContent 实现1、一级结构2、二级结构2.1、EmptyContent2.2、FileListContent2.2.1、ViewAction&#xff1a;2.2.2、EditAction2.2.3、DeleteAction2.2.4、ShareAction 五、载入起始页的时机五、总结 一…

Linux(Ubuntu24.04)源码编译安装VTK7.1.1记录

VTK&#xff08;Visualization Toolkit&#xff09;是一个开源的3D可视化开发工具包&#xff0c;用于开发可视化和图形处理应用程序。VTK提供了一系列的算法和工具&#xff0c;用于创建、渲染和处理复杂的3D图形和数据。VTK由C编写&#xff0c;并提供了Python、Java和Tcl等语言…

三、GIT与Github推送(上传)和克隆(下载)

GIT与Github推送&#xff08;上传&#xff09;和克隆&#xff08;下载&#xff09; 一、配置好SSH二、在Github创建仓库三、git克隆&#xff08;下载&#xff09;文件四、git推送&#xff08;上传&#xff09;文件到远程仓库 一、配置好SSH Git与Github上传和下载时需要使用到…

SpringCloudAlibaba实战入门之Sentinel服务降级和服务熔断(十五)

一、Sentinel概述 1、Sentinel是什么 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 一句话概括:sentinel即Hystrix的替代品,官网: https://sentinelguard.io/zh…

24年无人机行业资讯 | 12.23-12.29

24年无人机行业资讯 | 12.23-12.29 1、 国家发改委新设低空经济司&#xff0c;助力低空经济规范发展2、商务部支持无人机民用国际贸易&#xff0c;强调出口管制与安全并重3、滨州高新区首架无人机成功下线4、 2025第九届世界无人机大会筹备推进会顺利召开5、2024年世界无人机竞…

前端实现大文件上传(文件分片、文件hash、并发上传、断点续传、进度监控和错误处理,含nodejs)

大文件分片上传是前端一种常见的技术&#xff0c;用于提高大文件上传的效率和可靠性。主要原理和步骤如下 文件分片 确定分片大小&#xff1a;确定合适的分片大小。通常分片大小在 1MB 到 5MB 之间使用 Blob.slice 方法&#xff1a;将文件分割成多个分片。每个分片可以使用 Bl…

源代码编译安装X11及相关库、vim,配置vim(1)

一、目录结构 如下。 所有X11及相关库装到mybuild&#xff0c;源代码下载到src下&#xff0c;解压&#xff0c;进入&#xff0c;编译安装。编译时指定--prefix到相同的目录&#xff0c;即上图中mybuild。 ./configure --prefixpwd/../../mybuild [CFLAGS"-I/path/to/X11…

bytetrack 解决跟踪后框晃动的问题

使用距离最近的匹配的检测框 替代 bytetrack返回的跟踪框 作为最终的返回结果 完整byte_tracker.py代码为&#xff1a; import numpy as np from collections import deque import os import os.path as osp import copy import torch import torch.nn.functional as Ffrom …

如何使用OBS Studio录制屏幕?

可以进入官网或github进行下载&#xff1a; https://obsproject.com/download 安装包解压后进入bin 进入64-bit 选择obs 64 进入OBS Studio后在来源内右键&#xff0c;选择添加 选择添加显示器采集即可录取整个屏幕&#xff0c;窗口采集可选择窗口进行录制 选择对应显示器即配置…

ArcGIS Server 10.2授权文件过期处理

新的一年&#xff0c;arcgis server授权过期了&#xff0c;服务发不不了。查看ecp授权文件&#xff0c;原来的授权日期就到2024.12.31日。好吧&#xff0c;这里直接给出处理方法。 ArcGIS 10.2安装时&#xff0c;有的破解文件中会有含一个这样的注册程序&#xff0c;没有的话&…