還在用Spring Security?推薦你一款使用簡單、功能強大的權限認證框架
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:
??????#?連接池建立時創(chuàng)建的初始化連接數
??????initial-size:?1
??????#?連接池中最大的活躍連接數
??????max-active:?20
??????#?連接池中最小的活躍連接數
??????min-idle:?1
??????#?連接時最大等待時間,單位毫秒。配置了maxWait之后,缺省啟用公平鎖,并發(fā)效率會有所下降,如果需要可以通過配置useUnfairLock屬性為true使用非公平鎖。
??????max-wait:?60000
??????#?是否緩存preparedStatement,也就是PSCache。PSCache對支持游標的數據庫性能提升巨大,比如說oracle。在mysql下建議關閉。
??????pool-prepared-statements:?false
??????#?指定每個連接上PSCache的大小,要啟用PSCache,必須配置大于0,當大于0時,poolPreparedStatements自動觸發(fā)修改為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
??????#?是否在獲得連接后檢測其可用性,連接時執(zhí)行validationQuery檢測連接是否有效,做了這個配置會降低性能。
??????test-on-borrow:?false
??????#?是否在連接放回連接池后檢測其可用性,做了這個配置會降低性能。
??????test-on-return:?false
??????#?是否在連接空閑一段時間后檢測其可用性,建議配置為true,不影響性能,并且保證安全性。申請連接的時候檢測,如果空閑時間大于timeBetweenEvictionRunsMillis,執(zhí)行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
??????#?屬性類型是字符串,通過別名的方式配置擴展插件,常用的插件有:監(jiān)控統(tǒng)計用的filter:stat,日志用的filter:log4j,防御sql注入的filter:wall
??????filters:?stat
??????##########??StatViewServlet監(jiān)控配置??##########
??????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
??#?是否允許同一賬號并發(fā)登錄?(為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也能訪問接口
特別注意路由一定要有區(qū)分性,例如:/user和/user/{id} 這種方式satoken框架認為是同一個路由?。е侣酚设b權將兩個權限碼合并認證
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()
????????????????//?認證函數:?每次請求執(zhí)行
????????????????.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);
?????//?路由一定要有區(qū)分性
????????????????????SaRouter.match("/user",?()?->?StpUtil.checkPermission("0001"));
????????????????????SaRouter.match("/user/get/{id}",?()?->?StpUtil.checkPermission("001101"));
????????????????})
????????????????//?異常處理函數:每次認證函數發(fā)生異常時執(zhí)行此函數
????????????????.setError(e?->?{
????????????????????return?Result.failure(e.getMessage());
????????????????})
????????????????//?前置函數:在每次認證函數之前執(zhí)行
????????????????.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里面配置的有靜態(tài)文件讀取和跨域等,與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)
????????????????//?是否允許發(fā)送Cookie
????????????????.allowCredentials(true)
????????????????.allowedHeaders("*");
????}
????@Override
????public?void?addResourceHandlers(ResourceHandlerRegistry?registry)?{
????????//?靜態(tài)文件
????????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
推薦文章
評論
圖片
表情
