百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

重磅推荐:一款使用简单、功能强大的权限认证框架

ccwgpt 2024-10-27 08:53 41 浏览 0 评论

主要是Shiro、Security配置繁琐,这个简单易上手

官网地址

https://sa-token.dev33.cn/doc/index.html#/

主要是Shiro、Security配置繁琐,这个简单易上手

这是他的大致功能点,今天我们搞点基础的

springBoot 集成sa-token 并实现登录的验证和权限的鉴定

首先导入maven坐标

导入redis主要是sa-token使用内存来存取token的,使用redis第三方来做到重启项目token不丢,只需导入sa-token-redis的maven即可,不需要手动get,set

<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-spring-boot-starter</artifactId>
    <version>1.25.0</version>
</dependency>

<!-- sa-token整合redis (使用jdk默认序列化方式) -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-dao-redis</artifactId>
    <version>1.25.0</version>
</dependency>

yml配置文件配一下

server:
  port: 8010

spring:
  servlet:
    multipart:
      enabled: true
      location: C:/var/guoheng/picture/
      max-file-size: 10MB
      max-request-size: 10MB

  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/fire_control?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&allowMultiQueries=true&serverTimezone=GMT%2B8
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource
    #########  druid连接池配置  #########
    druid:
      # 连接池建立时创建的初始化连接数
      initial-size: 1
      # 连接池中最大的活跃连接数
      max-active: 20
      # 连接池中最小的活跃连接数
      min-idle: 1
      # 连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
      max-wait: 60000
      # 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
      pool-prepared-statements: false
      # 指定每个连接上PSCache的大小,要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100。
      max-pool-prepared-statement-per-connection-size: -1
      # 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。(不同数据库不同)
      validation-query: SELECT 'x'
      # 指定连接校验查询的超时时间,单位:秒。
      validation-query-timeout: 1
      # 是否在获得连接后检测其可用性,连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
      test-on-borrow: false
      # 是否在连接放回连接池后检测其可用性,做了这个配置会降低性能。
      test-on-return: false
      # 是否在连接空闲一段时间后检测其可用性,建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
      test-while-idle: true
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒。
      time-between-eviction-runs-millis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒。
      min-evictable-idle-time-millis: 300000
      # 登陆超时时间,单位是秒。
      login-timeout: 3
      # 查询超时时间,单位是秒。
      query-timeout: 3
      # 事务查询超时时间,单位是秒。
      transaction-query-timeout: 60
      # 异步关闭连接。
      async-close-connection-enable: true
      # 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:监控统计用的filter:stat,日志用的filter:log4j,防御sql注入的filter:wall
      filters: stat

      ##########  StatViewServlet监控配置  ##########
      stat-view-servlet:
        login-username: guoheng
        login-password: guoheng
        allow:
        deny:
  aop:
    auto: true

  ###################  redis配置  ###################
  redis:
    host: 127.0.0.1
    port: 6379
    password:
    jedis:
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 8
        min-idle: 0
        time-between-eviction-runs: 30000

  ################### sa-token配置 ###################
sa-token:
  # token名称 (同时也是cookie名称)
  token-name: satoken
  # token有效期,单位s 默认30天, -1代表永不过期
  timeout: 2592000
  # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
  activity-timeout: 3600
  # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
  is-concurrent: true
  # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
  is-share: false
  # token风格
  token-style: simple-uuid
  # 是否输出操作日志
  is-log: false


mybatis:
  mapper-locations: classpath*:mapper/*.xml

接下来是很重要的两个sa-token的config(使用过滤器的路由鉴权)

PS:拦截器鉴权N多坑,不传satoken也能访问接口

特别注意路由一定要有区分性,例如:/user和/user/{id} 这种方式satoken框架认为是同一个路由!!导致路由鉴权将两个权限码合并认证

package com.demo.app.config.satoken;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import result.Result;

import java.util.Arrays;


/**
 * @program: fire
 * @description:
 * @author: fbl
 * @create: 2021-08-31 12:15
 **/
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {

    /**
     * 注册 [sa-token全局过滤器]
     */
    @Bean
    public SaServletFilter getSaServletFilter() {
        return new SaServletFilter()

                // 指定 [拦截路由] 与 [放行路由]
                .addInclude("/**").addExclude()

                // 认证函数: 每次请求执行
                .setAuth(r -> {
                    System.out.println("---------- sa全局认证");
                    SaRouter.match(Arrays.asList("/**"), Arrays.asList(
                            "/login",
                            "/druid/**",
                            "/default/**",
                            "/",
                            "/swagger-ui.html",
                            "/swagger-resources/**",
                            "swagger/**",
                            "/webjars/**",
                            "/swagger-ui.html/*",
                            "/swagger-resources",
                            "/*.html",
                            "/**/*.html",
                            "/**/*.css",
                            "/**/*.js",
                            "/**/*.svg",
                            "/**/*.ico",
                            "/**/*.png",
                            "/**/*.jpg",
                            "/**/*.xlsx",
                            "/**/*.docx",
                            "/**/*.pdf",
                            "/webSocket/**",
                            "/*/api-docs",
                            "/v2/api-docs-ext"
                    ), StpUtil::checkLogin);
     // 路由一定要有区分性
                    SaRouter.match("/user", () -> StpUtil.checkPermission("0001"));
                    SaRouter.match("/user/get/{id}", () -> StpUtil.checkPermission("001101"));

                })

                // 异常处理函数:每次认证函数发生异常时执行此函数
                .setError(e -> {
                    return Result.failure(e.getMessage());
                })

                // 前置函数:在每次认证函数之前执行
                .setBeforeAuth(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-Frame-Options", "1; mode=block")
                            // 禁用浏览器内容嗅探
                            .setHeader("X-Content-Type-Options", "nosniff")
                    ;
                });

    }

}

这里是设置登录用户权限和角色的地方(从权限\角色表中查询放置),这里我只校验了权限,没有校验角色

package com.demo.app.config.satoken;

import cn.dev33.satoken.stp.StpInterface;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.demo.app.mapper.permission.PermissionMapper;
import com.demo.app.mapper.permission.RolePermissionMapper;
import com.demo.app.mapper.role.RoleMapper;
import com.demo.app.mapper.user.UserMapper;
import com.demo.app.mapper.user.UserRoleMapper;
import model.entity.sys.RolePermission;
import model.entity.sys.SysPermission;
import model.entity.sys.SysRole;
import model.entity.sys.UserRole;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.stream.Collectors;





/**
 * @program: fire
 * @description: 用户登录赋予相应权限
 * @author: fbl
 * @create: 2021-08-31 13:07
 **/
@Component
public class StpInterfaceImpl implements StpInterface {
    @Autowired
    UserMapper userMapper;

    @Autowired
    UserRoleMapper userRoleMapper;

    @Autowired
    RoleMapper roleMapper;

    @Autowired
    PermissionMapper permissionMapper;

    @Autowired
    RolePermissionMapper rolePermissionMapper;
    @Override
    public List<String> getPermissionList(Object userId, String s) {
        // 用户存在,查找角色
        QueryWrapper<UserRole> userRoleQueryWrapper = new QueryWrapper<>();
        userRoleQueryWrapper.eq("user_id", userId);
        List<UserRole> userRoles = userRoleMapper.selectList(userRoleQueryWrapper);

        // 角色查找权限
        QueryWrapper<RolePermission> rolePermissionQueryWrapper = new QueryWrapper<>();
        rolePermissionQueryWrapper.in("role_id", userRoles.stream().map(UserRole::getRoleId).collect(Collectors.toList()));
        List<RolePermission> rolePermissions = rolePermissionMapper.selectList(rolePermissionQueryWrapper);

        QueryWrapper<SysPermission> permissionQueryWrapper = new QueryWrapper<>();
        permissionQueryWrapper.in("id", rolePermissions.stream().map(RolePermission::getPermissionId).distinct().collect(Collectors.toList()));
        List<SysPermission> sysPermissions = permissionMapper.selectList(permissionQueryWrapper);

        List<String> permissions = sysPermissions.stream().map(SysPermission::getCode).distinct().collect(Collectors.toList());
        return permissions;

    }

    @Override
    public List<String> getRoleList(Object userId, String s) {
        // 用户存在,查找角色
        QueryWrapper<UserRole> userRoleQueryWrapper = new QueryWrapper<>();
        userRoleQueryWrapper.eq("user_id", userId);
        List<UserRole> userRoles = userRoleMapper.selectList(userRoleQueryWrapper);

        // 查询角色
        QueryWrapper<SysRole> sysRoleQueryWrapper = new QueryWrapper<SysRole>().in("id", userRoles.stream().map(UserRole::getRoleId).collect(Collectors.toList()));
        List<SysRole> sysRoles = roleMapper.selectList(sysRoleQueryWrapper);
        List<String> roleNames = sysRoles.stream().map(SysRole::getRoleName).distinct().collect(Collectors.toList());
        return roleNames;
    }
}

打这里认证鉴权就完成了,快把,赶紧来测试一下吧。

学习资料:Java进阶视频资源

还有一个配置文件冲突的问题,之前我在webMvc里面配置的有静态文件读取和跨域等,与satoken的配置起了冲突,我修改了自己的配置文件

package com.demo.app.config.webmvc;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 类功能描述: CorsConfig
 *
 * @author Eternal
 * @date 2019-11-26 15:11
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Value("${spring.servlet.multipart.location}")
    private String uploadFileUrl;

    /**
     * 跨域配置
     *
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("POST", "GET", "PUT", "DELETE", "OPTIONS")
                .maxAge(3600)
                // 是否允许发送Cookie
                .allowCredentials(true)
                .allowedHeaders("*");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 静态文件
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
        // swagger
        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");

        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        // 上传文件
        registry.addResourceHandler("/file/**").addResourceLocations("file:/" + uploadFileUrl);
    }
}

测试一下

拿到token放进header里取请求需要权限的接口

没有权限

拥有权限

还在用Spring Security?推荐你一款使用简单、功能强大的权限认证框架

相关推荐

RACI矩阵:项目管理中的角色与责任分配利器

作者:赵小燕RACI矩阵RACI矩阵是项目管理中的一种重要工具,旨在明确团队在各个任务中的角色和职责。通过将每个角色划分为负责人、最终责任人、咨询人和知情人四种类型,RACI矩阵确保每个人都清楚自己...

在弱矩阵组织中,如何做好项目管理工作?「慕哲制图」

慕哲出品必属精品系列在弱矩阵组织中,如何做好项目管理工作?【慕哲制图】-------------------------------慕哲制图系列0:一图掌握项目、项目集、项目组合、P2、商业分析和NP...

Scrum模式:每日站会(Daily Scrum)

定义每日站会(DailyScrum)是一个Scrum团队在进行Sprint期间的日常会议。这个会议的主要目的是为了应对Sprint计划中的不断变化,确保团队能够有效应对挑战并达成Sprint目标。为...

大家都在谈论的敏捷开发&amp;Scrum,到底是什么?

敏捷开发作为一种开发模式,近年来深受研发团队欢迎,与瀑布式开发相比,敏捷开发更轻量,灵活性更高,在当下多变环境下,越来越多团队选择敏捷开发。什么是敏捷?敏捷是一种在不确定和变化的环境中,通过创造和响应...

敏捷与Scrum是什么?(scrum敏捷开发是什么)

敏捷是一种思维模式和哲学,它描述了敏捷宣言中的一系列原则。另一方面,Scrum是一个框架,规定了实现这种思维方式的角色,事件,工件和规则/指南。换句话说,敏捷是思维方式,Scrum是规定实施敏捷哲学的...

敏捷项目管理与敏捷:Scrum流程图一览

敏捷开发中的Scrum流程通常可以用一个简单的流程图来表示,以便更清晰地展示Scrum框架的各个阶段和活动。以下是一个常见的Scrum流程图示例:这个流程图涵盖了Scrum框架的主要阶段和活动,其中包...

一张图掌握项目生命周期模型及Scrum框架

Mockito 的最佳实践(mock方法)

记得以前面试的时候,面试官问我,平常开发过程中自己会不会测试?我回答当然会呀,自己写的代码怎么不测呢。现在想想我好像误会他的意思了,他应该是想问我关于单元测试,集成测试以及背后相关的知识,然而当时说到...

EffectiveJava-5-枚举和注解(java枚举的作用与好处)

用enum代替int常量1.int枚举:引入枚举前,一般是声明一组具名的int常量,每个常量代表一个类型成员,这种方法叫做int枚举模式。int枚举模式是类型不安全的,例如下面两组常量:性别和动物种...

Maven 干货 全篇共:28232 字。预计阅读时间:110 分钟。建议收藏!

Maven简介Maven这个词可以翻译为“知识的积累”,也可以翻译为“专家”或“内行”。Maven是一个跨平台的项目管理工具。主要服务于基于Java平台的项目构建、依赖管理和项目信息管理。仔...

Java单元测试框架PowerMock学习(java单元测试是什么意思)

前言高德的技术大佬在谈论方法论时说到:“复杂的问题要简单化,简单的问题要深入化。”这句话让我感触颇深,这何尝不是一套编写代码的方法——把一个复杂逻辑拆分为许多简单逻辑,然后把每一个简单逻辑进行深入实现...

Spring框架基础知识-第六节内容(Spring高级话题)

Spring高级话题SpringAware基本概念Spring的依赖注入的最大亮点是你所有的Bean对Spring容器的存在是没有意识的。但是在实际的项目中,你的Bean必须要意识到Spring容器...

Java单元测试浅析(JUnit+Mockito)

作者:京东物流秦彪1.什么是单元测试(1)单元测试环节:测试过程按照阶段划分分为:单元测试、集成测试、系统测试、验收测试等。相关含义如下:1)单元测试:针对计算机程序模块进行输出正确性检验工作...

揭秘Java代码背后的质检双侠:JUnit与Mockito!

你有没有发现,现在我们用的手机App、逛的网站,甚至各种智能设备,功能越来越复杂,但用起来却越来越顺畅,很少遇到那种崩溃、卡顿的闹心事儿?这背后可不是程序员一拍脑袋写完代码就完事儿了!他们需要一套严谨...

单元测试框架哪家强?Junit来帮忙!

大家好,在前面的文章中,给大家介绍了以注解和XML的方式分别实现IOC和依赖注入。并且我们定义了一个测试类,通过测试类来获取到了容器中的Bean,具体的测试类定义如下:@Testpublicvoid...

取消回复欢迎 发表评论: