首先贴下官方文档,官方sa-token文档;看了一圈都是集成SpringBoot,我用bean注入看起来是先例哦哈哈,好咯,不跟你多bb,直接上代码
代码我放在了GITEE中了需要自取,传送门
- 改applicationContext.xml(只有增加的哦)
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:redis.properties"/>
<!-- Sa-Token-->
<bean class="org.springframework.boot.autoconfigure.EnableAutoConfiguration" abstract="true">
</bean>
<bean class="cn.dev33.satoken.spring.SaBeanRegister"/>
<bean class="cn.dev33.satoken.spring.SaBeanInject">
<property name="config" ref="sa-token"/>
</bean>
<!--自己看SaAloneRedisInject重写的路径-->
<bean class="top.jacktgq.controller.SaAloneRedisInject">
</bean>
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="300"/> <!--最大能够保持idel状态的对象数-->
<property name="maxTotal" value="60000"/><!--最大分配的对象数-->
<property name="testOnBorrow" value="true"/><!--当调用borrow Oject方法时,是否进行有效性检查-->
</bean>
<bean primary="true" id="redisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:hostName="${redis.host}"
p:port="${redis.port}"
p:database="${redis.database}"/>
<bean name="saTokenDao" class="cn.dev33.satoken.dao.SaTokenDaoRedisJackson">
</bean>
<!--sa-token配置-->
<bean id="sa-token" class="cn.dev33.satoken.config.SaTokenConfig">
<!--token名称 (同时也是cookie名称)-->
<property name="tokenName" value="satoken"></property>
<!--token有效期,单位s 默认30天, -1代表永不过期-->
<property name="timeout" value="2592000"></property>
<!--token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒-->
<property name="activityTimeout" value="-1"></property>
<!--是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)-->
<property name="isConcurrent" value="false"></property>
<!--在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)-->
<property name="isShare" value="true"></property>
<!--token风格-->
<property name="tokenStyle" value="uuid"></property>
<!--是否输出操作日志-->
<property name="isLog" value="true"/>
<property name="sso" ref="sso"/>
</bean>
<!--# SSO-相关配置-->
<bean id="sso" class="cn.dev33.satoken.config.SaSsoConfig">
<!-- # SSO-Server端 统一认证地址-->
<property name="authUrl" value="http://xka.com:8883/sso/auth"/>
<!--# 是否打开单点注销接口-->
<property name="isSlo" value="true"/>
</bean>
- 增加redis.properties
redis.host=127.0.0.1
redis.port=6379
redis.password=
redis.database=1
redis.maxActive=300
redis.maxWait=1000
redis.testOnBorrow=true
redis.timeout=100000
sa-token.alone-redis=6666
sa-token.alone-redis.host=6976
fep.local.cache.capacity =10000
- 重写SaAloneRedisInject类的方法,至于为什么重写,是因为,他有一个地方要获取SpringBoot的配置的 host,但是我们是SpringMVC的故而就直接重写它
package top.jacktgq.controller;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.dao.SaTokenDaoDefaultImpl;
import cn.dev33.satoken.dao.SaTokenDaoRedisJackson;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Lettuce;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import top.jacktgq.utils.PropertiesLoader;
/**
* 为 SaTokenDao 单独设置Redis连接信息
* @author kong
*/
@Configuration
public class SaAloneRedisInject implements EnvironmentAware {
/**
* 配置信息的前缀
*/
public static final String ALONE_PREFIX = "sa-token.alone-redis";
/**
* Sa-Token 持久层接口
*/
@Autowired(required = false)
public SaTokenDao saTokenDao;
/**
* 开始注入
*/
@Override
public void setEnvironment(Environment environment) {
try {
// 如果为空或者默认实现,则不进行任何操作
if(saTokenDao == null || saTokenDao instanceof SaTokenDaoDefaultImpl) {
return;
}
// 如果配置文件不包含相关配置,则不进行任何操作
/*if(environment.getProperty(ALONE_PREFIX + ".host") == null) {
return;
}*/
// ------------------- 开始注入
// 获取cfg对象
//RedisProperties cfg = Binder.get(environment).bind(ALONE_PREFIX, RedisProperties.class).get();
RedisProperties cfg = getSaAloneRedisConfig();
// 1. Redis配置
RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
redisConfig.setHostName(cfg.getHost());
redisConfig.setPort(cfg.getPort());
redisConfig.setDatabase(cfg.getDatabase());
redisConfig.setPassword(RedisPassword.of(cfg.getPassword()));
// 2. 连接池配置
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
// pool配置
Lettuce lettuce = cfg.getLettuce();
if(lettuce.getPool() != null) {
RedisProperties.Pool pool = cfg.getLettuce().getPool();
// 连接池最大连接数
poolConfig.setMaxTotal(pool.getMaxActive());
// 连接池中的最大空闲连接
poolConfig.setMaxIdle(pool.getMaxIdle());
// 连接池中的最小空闲连接
poolConfig.setMinIdle(pool.getMinIdle());
// 连接池最大阻塞等待时间(使用负值表示没有限制)
poolConfig.setMaxWaitMillis(pool.getMaxWait().toMillis());
}
LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration.builder();
// timeout
if(cfg.getTimeout() != null) {
builder.commandTimeout(cfg.getTimeout());
}
// shutdownTimeout
if(lettuce.getShutdownTimeout() != null) {
builder.shutdownTimeout(lettuce.getShutdownTimeout());
}
// 创建Factory对象
LettuceClientConfiguration clientConfig = builder.poolConfig(poolConfig).build();
LettuceConnectionFactory factory = new LettuceConnectionFactory(redisConfig, clientConfig);
factory.afterPropertiesSet();
// 3. 开始初始化 SaTokenDao
// 如果是SaTokenDaoRedis
try {
Class.forName("cn.dev33.satoken.dao.SaTokenDaoRedis");
SaTokenDaoRedis dao = (SaTokenDaoRedis)saTokenDao;
dao.isInit = false;
dao.init(factory);
return;
} catch (ClassNotFoundException e) {
}
// 如果是SaTokenDaoRedisJackson
try {
Class.forName("cn.dev33.satoken.dao.SaTokenDaoRedisJackson");
SaTokenDaoRedisJackson dao = (SaTokenDaoRedisJackson)saTokenDao;
dao.isInit = false;
dao.init(factory);
return;
} catch (ClassNotFoundException e) {
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 骗过编辑器,增加配置文件代码提示
* 自定义编写
* @return 配置对象
*/
@ConfigurationProperties(prefix = ALONE_PREFIX)
public RedisProperties getSaAloneRedisConfig() {
RedisProperties redisProperties =new RedisProperties();
PropertiesLoader propertiesLoader = new PropertiesLoader("redis.properties");
redisProperties.setHost(propertiesLoader.getProperty("redis.host"));
redisProperties.setPort(Integer.parseInt(propertiesLoader.getProperty("redis.port")));
redisProperties.setDatabase(Integer.parseInt(propertiesLoader.getProperty("redis.database")));
redisProperties.setPassword(propertiesLoader.getProperty("redis.password"));
return redisProperties;
}
}
- Sa-Token持久层接口 SaTokenDaoRedis
package top.jacktgq.controller;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.util.SaFoxUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Sa-Token持久层接口 [Redis版 (使用JDK默认序列化方式)]
*
* @author kong
*
*/
@Component
public class SaTokenDaoRedis implements SaTokenDao {
/**
* String专用
*/
public StringRedisTemplate stringRedisTemplate;
/**
* Objecy专用
*/
public RedisTemplate<String, Object> objectRedisTemplate;
/**
* 标记:是否已初始化成功
*/
public boolean isInit;
@Autowired
public void init(RedisConnectionFactory connectionFactory) {
// 指定相应的序列化方案
StringRedisSerializer keySerializer = new StringRedisSerializer();
JdkSerializationRedisSerializer valueSerializer = new JdkSerializationRedisSerializer();
// 构建StringRedisTemplate
StringRedisTemplate stringTemplate = new StringRedisTemplate();
stringTemplate.setConnectionFactory(connectionFactory);
stringTemplate.afterPropertiesSet();
// 构建RedisTemplate
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(keySerializer);
template.setHashKeySerializer(keySerializer);
template.setValueSerializer(valueSerializer);
template.setHashValueSerializer(valueSerializer);
template.afterPropertiesSet();
// 开始初始化相关组件
if(this.isInit == false) {
this.stringRedisTemplate = stringTemplate;
this.objectRedisTemplate = template;
this.isInit = true;
}
}
/**
* 获取Value,如无返空
*/
@Override
public String get(String key) {
return stringRedisTemplate.opsForValue().get(key);
}
/**
* 写入Value,并设定存活时间 (单位: 秒)
*/
@Override
public void set(String key, String value, long timeout) {
if(timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) {
return;
}
// 判断是否为永不过期
if(timeout == SaTokenDao.NEVER_EXPIRE) {
stringRedisTemplate.opsForValue().set(key, value);
} else {
stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
}
}
/**
* 修改指定key-value键值对 (过期时间不变)
*/
@Override
public void update(String key, String value) {
long expire = getTimeout(key);
// -2 = 无此键
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
return;
}
this.set(key, value, expire);
}
/**
* 删除Value
*/
@Override
public void delete(String key) {
stringRedisTemplate.delete(key);
}
/**
* 获取Value的剩余存活时间 (单位: 秒)
*/
@Override
public long getTimeout(String key) {
return stringRedisTemplate.getExpire(key);
}
/**
* 修改Value的剩余存活时间 (单位: 秒)
*/
@Override
public void updateTimeout(String key, long timeout) {
// 判断是否想要设置为永久
if(timeout == SaTokenDao.NEVER_EXPIRE) {
long expire = getTimeout(key);
if(expire == SaTokenDao.NEVER_EXPIRE) {
// 如果其已经被设置为永久,则不作任何处理
} else {
// 如果尚未被设置为永久,那么再次set一次
this.set(key, this.get(key), timeout);
}
return;
}
stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
}
/**
* 获取Object,如无返空
*/
@Override
public Object getObject(String key) {
return objectRedisTemplate.opsForValue().get(key);
}
/**
* 写入Object,并设定存活时间 (单位: 秒)
*/
@Override
public void setObject(String key, Object object, long timeout) {
if(timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) {
return;
}
// 判断是否为永不过期
if(timeout == SaTokenDao.NEVER_EXPIRE) {
objectRedisTemplate.opsForValue().set(key, object);
} else {
objectRedisTemplate.opsForValue().set(key, object, timeout, TimeUnit.SECONDS);
}
}
/**
* 更新Object (过期时间不变)
*/
@Override
public void updateObject(String key, Object object) {
long expire = getObjectTimeout(key);
// -2 = 无此键
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
return;
}
this.setObject(key, object, expire);
}
/**
* 删除Object
*/
@Override
public void deleteObject(String key) {
objectRedisTemplate.delete(key);
}
/**
* 获取Object的剩余存活时间 (单位: 秒)
*/
@Override
public long getObjectTimeout(String key) {
return objectRedisTemplate.getExpire(key);
}
/**
* 修改Object的剩余存活时间 (单位: 秒)
*/
@Override
public void updateObjectTimeout(String key, long timeout) {
// 判断是否想要设置为永久
if(timeout == SaTokenDao.NEVER_EXPIRE) {
long expire = getObjectTimeout(key);
if(expire == SaTokenDao.NEVER_EXPIRE) {
// 如果其已经被设置为永久,则不作任何处理
} else {
// 如果尚未被设置为永久,那么再次set一次
this.setObject(key, this.getObject(key), timeout);
}
return;
}
objectRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
}
/**
* 搜索数据
*/
@Override
public List<String> searchData(String prefix, String keyword, int start, int size) {
Set<String> keys = stringRedisTemplate.keys(prefix + "*" + keyword + "*");
List<String> list = new ArrayList<String>(keys);
return SaFoxUtil.searchList(list, start, size);
}
}
- 最后一点就是配置拦截器的了,@EnableWebMvc这个注解一定要加,不然没有用,我因为这个问题困扰2天
package top.jacktgq.controller;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
import cn.dev33.satoken.interceptor.SaRouteInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.spring.SpringMVCUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaFoxUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import top.jacktgq.security.util.StpMemberUtil;
import java.util.Arrays;
@Configuration
@EnableWebMvc//开启SpringMVC支持,如无此注解,重写WebMvcConfigurerAdapter类的方法无效
@ComponentScan("top.jacktgq.controller")
public class JthinkTokenConfig implements WebMvcConfigurer {
// 注册sa-token的拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 启用注解可以和下面路由拦截器(SaRouteInterceptor)同时使用
registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
// 注册路由拦截器,自定义验证规则
registry.addInterceptor(new SaRouteInterceptor((request, response, handler) -> {
// 登录验证 -- 排除多个路径
SaRouter.match(Arrays.asList("/admin/index/**"),
Arrays.asList("/static/**", "/system/adminlogin", "/system/dologin", "/adminlogout"),
() -> StpUtil.checkLogin());
/* // 角色认证 -- 拦截以 admin 开头的路由,必须具备[admin]角色或者[super-admin]角色才可以通过认证
SaRouter.match("/admin/**", () -> StpUtil.checkRoleOr("admin", "super-admin"));
// 权限认证 -- 不同模块, 校验不同权限
SaRouter.match("/user/**", () -> StpUtil.checkPermission("user"));
SaRouter.match("/admin/**", () -> StpUtil.checkPermission("admin"));
SaRouter.match("/goods/**", () -> StpUtil.checkPermission("goods"));
SaRouter.match("/orders/**", () -> StpUtil.checkPermission("orders"));
SaRouter.match("/notice/**", () -> StpUtil.checkPermission("notice"));
SaRouter.match("/comment/**", () -> StpUtil.checkPermission("comment"));
// 匹配 restful 风格路由
SaRouter.match("/article/get/{id}", () -> StpUtil.checkPermission("article"));*/
// 在多账号模式下,可以使用任意StpUtil进行校验
SaRouter.match(Arrays.asList("/member/**"),
Arrays.asList("/member/login", "/member/dologin", "/member/register", "/member/logout"),
() -> StpMemberUtil.checkLogin());
})).addPathPatterns("/**");
}
/** 注册 [Sa-Token全局过滤器] */
@Bean
public SaServletFilter getSaServletFilter() {
return new SaServletFilter()
.addInclude("/**")
.addExclude("/sso/*", "/favicon.ico")
.setAuth(obj -> {
if(StpUtil.isLogin() == false) {
String back = SaFoxUtil.joinParam(SaHolder.getRequest().getUrl(), SpringMVCUtil.getRequest().getQueryString());
SaHolder.getResponse().redirect("/sso/login?back=" + SaFoxUtil.encodeUrl(back));
SaRouter.back();
}
})
;
}
}
最后,上面基本上都配置好了,总结就是多看源码,多看文档,如果有老鸟发现博客有问题的话,大家评论一下,愚人节快乐