还在用Spring Security?推荐你一款使用简单、功能强大的权限认证框架
共 11778字,需浏览 24分钟
·
2021-10-18 19:28
Java 8 一行代码解决了空指针问题,太厉害了... List中remove()方法的陷阱,被坑惨了! 25000 字详解 23 种设计模式,原来可以这么简单! 最牛逼的 Java 日志框架,性能无敌,横扫所有对手..... 这玩意比ThreadLocal叼多了,吓得我赶紧分享出来。
来源:blog.csdn.net/qq_40058629/article/
details/116692302
我们先看一下官网介绍,sa-token有什么功能
官网地址
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
<dependency>
<groupId>cn.dev33groupId>
<artifactId>sa-token-spring-boot-starterartifactId>
<version>1.25.0version>
dependency>
<dependency>
<groupId>cn.dev33groupId>
<artifactId>sa-token-dao-redisartifactId>
<version>1.25.0version>
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 getPermissionList(Object userId, String s) {
// 用户存在,查找角色
QueryWrapper userRoleQueryWrapper = new QueryWrapper<>();
userRoleQueryWrapper.eq("user_id", userId);
List userRoles = userRoleMapper.selectList(userRoleQueryWrapper);
// 角色查找权限
QueryWrapper rolePermissionQueryWrapper = new QueryWrapper<>();
rolePermissionQueryWrapper.in("role_id", userRoles.stream().map(UserRole::getRoleId).collect(Collectors.toList()));
List rolePermissions = rolePermissionMapper.selectList(rolePermissionQueryWrapper);
QueryWrapper permissionQueryWrapper = new QueryWrapper<>();
permissionQueryWrapper.in("id", rolePermissions.stream().map(RolePermission::getPermissionId).distinct().collect(Collectors.toList()));
List sysPermissions = permissionMapper.selectList(permissionQueryWrapper);
List permissions = sysPermissions.stream().map(SysPermission::getCode).distinct().collect(Collectors.toList());
return permissions;
}
@Override
public List getRoleList(Object userId, String s) {
// 用户存在,查找角色
QueryWrapper userRoleQueryWrapper = new QueryWrapper<>();
userRoleQueryWrapper.eq("user_id", userId);
List userRoles = userRoleMapper.selectList(userRoleQueryWrapper);
// 查询角色
QueryWrapper sysRoleQueryWrapper = new QueryWrapper().in("id", userRoles.stream().map(UserRole::getRoleId).collect(Collectors.toList()));
List sysRoles = roleMapper.selectList(sysRoleQueryWrapper);
List 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里取请求需要权限的接口
没有权限
拥有权限
最后一点:用户登录过后,header中不用传satoken也能进行鉴权(自动拿的是登录的用户的satoken),多个用户登录拿的是最后一个用户登录的satoken,好扯,也就是说必须要传satoken
推荐文章