Spring Boot + Redis + Sa-Token

ops/2025/1/19 2:37:00/

参考文献

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 {@Overridepublic String getLoginId() {// 返回当前登录用户的IDreturn "123"; // 假设返回用户ID为123}@Overridepublic boolean isLogin() {// 判断当前用户是否登录return true; // 假设用户已登录}@Overridepublic void login(Object loginId) {// 实现用户登录逻辑// 这里可以根据传入的loginId来设置用户的登录状态}@Overridepublic void logout() {// 实现登出逻辑// 清除用户的登录状态}@Overridepublic boolean hasPermission(String permission) {// 判断用户是否有某个权限return "admin".equals(permission); // 假设只有管理员有权限}@Overridepublic 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 -> USERINSERT 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/doLoginusername=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.ppmy.cn/ops/151251.html

相关文章

浅谈云计算18 | OpenStack架构概述

OpenStack架构概述 一、OpenStack核心组件探究1.1 计算组件Nova1.2 镜像组件Glance1.3 身份认证组件Keystone1.4 网络组件Neutron1.5 块存储组件Cinder1.6 对象存储组件Swift1.7 控制面板组件Horizon1.8 计量组件Ceilometer1.9 编排组件Heat 二、OpenStack组件逻辑关系揭秘2.1 …

微信小程序码生成

微信小程序生成二维码、程序码、海报_java生成二维码分享海报-CSDN博客

DNS介绍(5):DNS 劫持及解决方案

DNS劫持分析 DNS劫持&#xff0c;亦称为域名重定向或DNS篡改&#xff0c;是一种网络攻击技术。其核心在于攻击者通过篡改DNS系统的域名解析结果&#xff0c;将用户本意访问的域名指向攻击者所掌控的IP地址。这种攻击不仅可能使用户无法顺利访问所需网站&#xff0c;还可能让用…

MongoDB单机版安装

MongoDB单机版安装 在CentOS Linux release 7.9.2009 (Core)下安装MongoDB的步骤如下&#xff1a; 1 创建用户和组&#xff08;可选&#xff0c;根据需要&#xff09; 如果您希望以非root用户运行MongoDB服务&#xff0c;可以创建一个专用的用户和组。 groupadd mongodb us…

数据仓库基础常见面试题

1.数据仓库是什么 ‌数据仓库&#xff08;Data Warehouse&#xff09;是一个面向主题的、集成的、非易失的、随时间变化的数据集合&#xff0c;用于支持企业的管理决策‌。它不同于传统的操作型数据库&#xff0c;后者主要用于处理日常业务交易和实时查询&#xff0c;而数据仓库…

将内部部署系统的端口暴露给外部访问,并且仅允许指定 IP 的服务器访问该端口

以下是实现将内部部署系统的端口暴露给外部访问&#xff0c;并且仅允许指定 IP 的服务器访问该端口的步骤和思路&#xff1a; 一、网络架构调整 防火墙设置&#xff1a; 大多数内部网络都有防火墙&#xff0c;首先需要在防火墙上打开所需的端口&#xff0c;但要限制访问该端口…

“AI 自动化效能评估系统:开启企业高效发展新征程

在当今数字化飞速发展的时代&#xff0c;企业面临着日益激烈的市场竞争&#xff0c;如何提升效率、降低成本成为了企业生存与发展的关键。AI 自动化效能评估系统应运而生&#xff0c;它如同一把智能钥匙&#xff0c;为企业开启了高效发展的新征程。 AI 自动化效能评估系统&…

基于 Electron 应用的安全测试基础 — 提取和分析 .asar 文件

视频教程在我主页简介或专栏里 目录&#xff1a; 提取和分析 .asar 文件 4.1. .asar 文件提取工具 4.1.1. 为什么选择 NPX&#xff1f; 4.2. 提取过程 4.3. 提取 .asar 文件的重要性 4.3.1 关键词 4.3.2 执行关键词搜索 4.3.2.1 使用命令行工具“grep”进行关键词搜索 4.3.2…