mirror of
https://gitee.com/dromara/sa-token.git
synced 2026-02-27 16:50:24 +08:00
第一版
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
package cn.dev33.satoken;
|
||||
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.config.SaTokenConfigFactory;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.dao.SaTokenDaoDefault;
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import cn.dev33.satoken.stp.StpInterfaceDefaultImpl;
|
||||
|
||||
/**
|
||||
* 管理sa-token所有对象
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenManager {
|
||||
|
||||
// 配置文件 Bean
|
||||
private static SaTokenConfig config;
|
||||
public static SaTokenConfig getConfig() {
|
||||
if (config == null) {
|
||||
initConfig();
|
||||
}
|
||||
return config;
|
||||
}
|
||||
public static void setConfig(SaTokenConfig config) {
|
||||
SaTokenManager.config = config;
|
||||
if(config.getIsV()) {
|
||||
SaTokenUtil.printSaToken();
|
||||
}
|
||||
}
|
||||
public synchronized static void initConfig() {
|
||||
if (config == null) {
|
||||
setConfig(SaTokenConfigFactory.createConfig());
|
||||
}
|
||||
}
|
||||
|
||||
// 持久化 Bean
|
||||
public static SaTokenDao dao;
|
||||
public static SaTokenDao getDao() {
|
||||
if (dao == null) {
|
||||
initDao();
|
||||
}
|
||||
return dao;
|
||||
}
|
||||
public static void setDao(SaTokenDao dao) {
|
||||
SaTokenManager.dao = dao;
|
||||
}
|
||||
public synchronized static void initDao() {
|
||||
if (dao == null) {
|
||||
setDao(new SaTokenDaoDefault());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 权限认证 Bean
|
||||
public static StpInterface stp;
|
||||
public static StpInterface getStp() {
|
||||
if (stp == null) {
|
||||
initStp();
|
||||
}
|
||||
return stp;
|
||||
}
|
||||
public static void setStp(StpInterface stp) {
|
||||
SaTokenManager.stp = stp;
|
||||
}
|
||||
public synchronized static void initStp() {
|
||||
if (stp == null) {
|
||||
setStp(new StpInterfaceDefaultImpl());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
30
sa-token-dev/src/main/java/cn/dev33/satoken/SaTokenUtil.java
Normal file
30
sa-token-dev/src/main/java/cn/dev33/satoken/SaTokenUtil.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package cn.dev33.satoken;
|
||||
|
||||
/**
|
||||
* sa-token 工具类
|
||||
*/
|
||||
public class SaTokenUtil {
|
||||
|
||||
|
||||
// sa-token 版本号
|
||||
public static final String version = "v1.0.0";
|
||||
|
||||
// sa-token 开源地址
|
||||
public static final String github_url = "https://github.com/click33/sa-token";
|
||||
|
||||
// 打印 sa-token
|
||||
public static void printSaToken() {
|
||||
String str =
|
||||
"____ ____ ___ ____ _ _ ____ _ _ \r\n" +
|
||||
"[__ |__| __ | | | |_/ |___ |\\ | \r\n" +
|
||||
"___] | | | |__| | \\_ |___ | \\| \r\n" +
|
||||
"sa-token:" + version + " \r\n" +
|
||||
"GitHub:" + github_url + "\r\n";
|
||||
System.out.println(str);
|
||||
}
|
||||
|
||||
// 如果token为本次请求新创建的,则以此字符串为key存储在当前request中
|
||||
public static final String just_created_save_key= "just_created_save_key_";
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package cn.dev33.satoken.config;
|
||||
|
||||
/**
|
||||
* sa-token 总配置类
|
||||
*/
|
||||
public class SaTokenConfig {
|
||||
|
||||
private String tokenName = "satoken"; // token名称(同时也是cookie名称)
|
||||
private long timeout = 30 * 24 * 60 * 60; // token有效期,单位s 默认30天,-1为永不过期
|
||||
private Boolean isShare = true; // 在多人登录同一账号时,是否共享会话(为true时共用一个,为false时新登录挤掉旧登录)
|
||||
private Boolean isReadHead = false; // 是否在cookie读取不到token时,继续从请求header里继续尝试读取
|
||||
private Boolean isReadBody = false; // 是否在header读取不到token时,继续从请求题参数里继续尝试读取
|
||||
|
||||
private Boolean isV = true; // 是否在初始化配置时打印版本字符画
|
||||
|
||||
|
||||
public String getTokenName() {
|
||||
return tokenName;
|
||||
}
|
||||
public void setTokenName(String tokenName) {
|
||||
this.tokenName = tokenName;
|
||||
}
|
||||
public long getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
public void setTimeout(long timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
public Boolean getIsShare() {
|
||||
return isShare;
|
||||
}
|
||||
public void setIsShare(Boolean isShare) {
|
||||
this.isShare = isShare;
|
||||
}
|
||||
public Boolean getIsReadHead() {
|
||||
return isReadHead;
|
||||
}
|
||||
public void setIsReadHead(Boolean isReadHead) {
|
||||
this.isReadHead = isReadHead;
|
||||
}
|
||||
public Boolean getIsReadBody() {
|
||||
return isReadBody;
|
||||
}
|
||||
public void setIsReadBody(Boolean isReadBody) {
|
||||
this.isReadBody = isReadBody;
|
||||
}
|
||||
public Boolean getIsV() {
|
||||
return isV;
|
||||
}
|
||||
public void setIsV(Boolean isV) {
|
||||
this.isV = isV;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaTokenConfig [tokenName=" + tokenName + ", timeout=" + timeout + ", isShare=" + isShare
|
||||
+ ", isReadHead=" + isReadHead + ", isReadBody=" + isReadBody + ", isV=" + isV + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package cn.dev33.satoken.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
创建一个配置文件
|
||||
*/
|
||||
public class SaTokenConfigFactory {
|
||||
|
||||
|
||||
public static String configPath = "sa-token.properties"; // 默认配置文件地址
|
||||
|
||||
/**
|
||||
* 根据指定路径获取配置信息
|
||||
* @return 一个SaTokenConfig对象
|
||||
*/
|
||||
public static SaTokenConfig createConfig() {
|
||||
Map<String, String> map = readPropToMap(configPath);
|
||||
if(map == null){
|
||||
// throw new RuntimeException("找不到配置文件:" + configPath, null);
|
||||
}
|
||||
return (SaTokenConfig)initPropByMap(map, new SaTokenConfig());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 将指定路径的properties配置文件读取到Map中
|
||||
* @param propertiesPath 配置文件地址
|
||||
* @return 一个Map
|
||||
*/
|
||||
private static Map<String, String> readPropToMap(String propertiesPath){
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
try {
|
||||
InputStream is = SaTokenConfigFactory.class.getClassLoader().getResourceAsStream(propertiesPath);
|
||||
if(is == null){
|
||||
return null;
|
||||
}
|
||||
Properties prop = new Properties();
|
||||
prop.load(is);
|
||||
for (String key : prop.stringPropertyNames()) {
|
||||
map.put(key, prop.getProperty(key));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("配置文件(" + propertiesPath + ")加载失败", e);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将 Map 的值映射到 Model 上
|
||||
* @param map 属性集合
|
||||
* @param obj 对象,或类型
|
||||
* @return 返回实例化后的对象
|
||||
*/
|
||||
private static Object initPropByMap(Map<String, String> map, Object obj){
|
||||
|
||||
if(map == null){
|
||||
map = new HashMap<>();
|
||||
}
|
||||
|
||||
// 1、取出类型
|
||||
Class<?> cs = null;
|
||||
if(obj instanceof Class){ // 如果是一个类型,则将obj=null,以便完成静态属性反射赋值
|
||||
cs = (Class<?>)obj;
|
||||
obj = null;
|
||||
}else{ // 如果是一个对象,则取出其类型
|
||||
cs = obj.getClass();
|
||||
}
|
||||
|
||||
// 2、遍历类型属性,反射赋值
|
||||
for (Field field : cs.getDeclaredFields()) {
|
||||
String value = map.get(field.getName());
|
||||
if (value == null) {
|
||||
continue; // 如果为空代表没有配置此项
|
||||
}
|
||||
try {
|
||||
Object valueConvert = getObjectByClass(value, field.getType()); // 转换值类型
|
||||
field.setAccessible(true);
|
||||
field.set(obj, valueConvert);
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
throw new RuntimeException("属性赋值出错:" + field.getName(), e);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转化为指定数据类型
|
||||
* @param str 值
|
||||
* @param cs 要转换的类型
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T>T getObjectByClass(String str, Class<T> cs){
|
||||
Object value = null;
|
||||
if(str == null){
|
||||
value = null;
|
||||
}else if (cs.equals(String.class)) {
|
||||
value = str;
|
||||
} else if (cs.equals(int.class)||cs.equals(Integer.class)) {
|
||||
value = new Integer(str);
|
||||
} else if (cs.equals(long.class)||cs.equals(Long.class)) {
|
||||
value = new Long(str);
|
||||
} else if (cs.equals(short.class)||cs.equals(Short.class)) {
|
||||
value = new Short(str);
|
||||
} else if (cs.equals(float.class)||cs.equals(Float.class)) {
|
||||
value = new Float(str);
|
||||
} else if (cs.equals(double.class)||cs.equals(Double.class)) {
|
||||
value = new Double(str);
|
||||
} else if (cs.equals(boolean.class)||cs.equals(Boolean.class)) {
|
||||
value = new Boolean(str);
|
||||
}else{
|
||||
throw new RuntimeException("未能将值:" + str + ",转换类型为:" + cs, null);
|
||||
}
|
||||
return (T)value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package cn.dev33.satoken.dao;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
|
||||
/**
|
||||
* sa-token持久层的接口
|
||||
*/
|
||||
public interface SaTokenDao {
|
||||
|
||||
|
||||
/**
|
||||
* 根据key获取value ,如果没有,则返回空
|
||||
* @param key 键名称
|
||||
* @return
|
||||
*/
|
||||
public String getValue(String key);
|
||||
|
||||
/**
|
||||
* 写入指定key-value键值对,并设定过期时间 (单位:秒)
|
||||
* @param key 键名称
|
||||
* @param value 值
|
||||
* @param timeout 过期时间,单位:s
|
||||
*/
|
||||
public void setValue(String key, String value, long timeout);
|
||||
|
||||
/**
|
||||
* 删除一个指定的key
|
||||
* @param key
|
||||
*/
|
||||
public void delKey(String key);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据指定key的session,如果没有,则返回空
|
||||
* @param key 键名称
|
||||
* @return
|
||||
*/
|
||||
public SaSession getSaSession(String sessionId);
|
||||
|
||||
/**
|
||||
* 将指定session持久化
|
||||
* @param key 键名称
|
||||
* @param value 值
|
||||
* @param timeout 过期时间,单位: s
|
||||
*/
|
||||
public void saveSaSession(SaSession session, long timeout);
|
||||
|
||||
/**
|
||||
* 更新指定session
|
||||
*/
|
||||
public void updateSaSession(SaSession session);
|
||||
|
||||
/**
|
||||
* 删除一个指定的session
|
||||
* @param key 键名称
|
||||
*/
|
||||
public void delSaSession(String sessionId);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package cn.dev33.satoken.dao;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
|
||||
/**
|
||||
* sa-token持久层默认的实现类 , 基于内存Map
|
||||
*/
|
||||
public class SaTokenDaoDefault implements SaTokenDao {
|
||||
|
||||
/**
|
||||
* 所有数据集合
|
||||
*/
|
||||
Map<String, Object> dataMap = new HashMap<String, Object>();
|
||||
|
||||
|
||||
@Override
|
||||
public String getValue(String key) {
|
||||
return (String)dataMap.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String key, String value, long timeout) {
|
||||
dataMap.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delKey(String key) {
|
||||
dataMap.remove(key);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SaSession getSaSession(String sessionId) {
|
||||
return (SaSession)dataMap.get(sessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveSaSession(SaSession session, long timeout) {
|
||||
dataMap.put(session.getId(), session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSaSession(SaSession session) {
|
||||
// 无动作
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delSaSession(String sessionId) {
|
||||
dataMap.remove(sessionId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package cn.dev33.satoken.exception;
|
||||
|
||||
/**
|
||||
* 没有登陆抛出的异常
|
||||
*/
|
||||
public class NotLoginException extends RuntimeException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 6806129545290130142L;
|
||||
|
||||
/**
|
||||
* 创建一个
|
||||
*/
|
||||
public NotLoginException() {
|
||||
super("当前账号未登录");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package cn.dev33.satoken.exception;
|
||||
|
||||
/**
|
||||
* 没有指定权限码,抛出的异常
|
||||
*/
|
||||
public class NotPermissionException extends RuntimeException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 6806129545290130142L;
|
||||
|
||||
private Object code;
|
||||
|
||||
|
||||
/**
|
||||
* @return 获得权限码
|
||||
*/
|
||||
public Object getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public NotPermissionException(Object code) {
|
||||
super("无此权限:" + code);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
// public NotPermissionException(Object code, String s) {
|
||||
// super(s);
|
||||
// this.code = code;
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package cn.dev33.satoken.session;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
|
||||
|
||||
/**
|
||||
* session会话
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaSession implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String id; // 会话id
|
||||
private long createTime; // 当前会话创建时间
|
||||
private Map<String, Object> dataMap; // 当前会话键值对
|
||||
|
||||
|
||||
/**
|
||||
* 构建一个 session对象
|
||||
* @param id
|
||||
*/
|
||||
public SaSession(String id) {
|
||||
this.id = id;
|
||||
this.createTime = System.currentTimeMillis();
|
||||
this.dataMap = new HashMap<String, Object>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话id
|
||||
* @return
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前会话创建时间
|
||||
*/
|
||||
public long getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入值
|
||||
*/
|
||||
public void setAttribute(String key, Object value) {
|
||||
dataMap.put(key, value);
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 取值
|
||||
*/
|
||||
public Object getAttribute(String key) {
|
||||
return dataMap.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取值,并指定取不到值时的默认值
|
||||
*/
|
||||
public Object getAttribute(String key, Object default_value) {
|
||||
Object value = getAttribute(key);
|
||||
if(value != null) {
|
||||
return value;
|
||||
}
|
||||
return default_value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 移除一个key
|
||||
*/
|
||||
public void removeAttribute(String key) {
|
||||
dataMap.remove(key);
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有key
|
||||
*/
|
||||
public void clearAttribute() {
|
||||
dataMap.clear();
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否含有指定key
|
||||
*/
|
||||
public boolean containsAttribute(String key) {
|
||||
return dataMap.keySet().contains(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前session会话所有key
|
||||
*/
|
||||
public Set<String> getAttributeKeys() {
|
||||
return dataMap.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据集合(如果更新map里的值,请调用session.update()方法避免数据过时 )
|
||||
*/
|
||||
public Map<String, Object> getDataMap() {
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将这个session从持久库更新一下
|
||||
*/
|
||||
public void update() {
|
||||
SaTokenManager.getDao().updateSaSession(this);
|
||||
}
|
||||
|
||||
|
||||
// /** 注销会话(注销后,此session会话将不再存储服务器上) */
|
||||
// public void logout() {
|
||||
// SaTokenManager.getDao().delSaSession(this.id);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.dev33.satoken.session;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
|
||||
/**
|
||||
* sa-session工具类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaSessionUtil {
|
||||
|
||||
// 添加上指定前缀,防止恶意伪造session
|
||||
public static String session_key = "custom";
|
||||
public static String getSessionKey(String sessionId) {
|
||||
return SaTokenManager.getConfig().getTokenName() + ":" + session_key + ":session:" + sessionId;
|
||||
}
|
||||
|
||||
/** 指定key的session是否存在 */
|
||||
public boolean isExists(String sessionId) {
|
||||
return SaTokenManager.getDao().getSaSession(getSessionKey(sessionId)) != null;
|
||||
}
|
||||
|
||||
/** 获取指定key的session, 如果没有,is_create=是否新建并返回 */
|
||||
public static SaSession getSessionById(String sessionId, boolean is_create) {
|
||||
SaSession session = SaTokenManager.getDao().getSaSession(getSessionKey(sessionId));
|
||||
if(session == null && is_create) {
|
||||
session = new SaSession(getSessionKey(sessionId));
|
||||
SaTokenManager.getDao().saveSaSession(session, SaTokenManager.getConfig().getTimeout());
|
||||
}
|
||||
return session;
|
||||
}
|
||||
/** 获取指定key的session, 如果没有则新建并返回 */
|
||||
public static SaSession getSessionById(String sessionId) {
|
||||
return getSessionById(sessionId, true);
|
||||
}
|
||||
|
||||
/** 删除指定key的session */
|
||||
public static void delSessionById(String sessionId) {
|
||||
SaTokenManager.getDao().delSaSession(getSessionKey(sessionId));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package cn.dev33.satoken.spring;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* 将此注解加到springboot启动类上,即可完成sa-token与springboot的集成
|
||||
*/
|
||||
@Documented
|
||||
@Target({java.lang.annotation.ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Configuration
|
||||
@Import({SpringSaToken.class})
|
||||
public @interface SaTokenSetup {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package cn.dev33.satoken.spring;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
|
||||
/**
|
||||
* 与SpringBoot集成, 保证此类被扫描,即可完成sa-token与SpringBoot的集成
|
||||
* @author kongyongshun
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class SpringSaToken {
|
||||
|
||||
|
||||
// 获取配置Bean
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix="spring.sa-token")
|
||||
public SaTokenConfig getSaTokenConfig() {
|
||||
return new SaTokenConfig();
|
||||
}
|
||||
|
||||
// 注入配置Bean
|
||||
@Autowired
|
||||
public void setConfig(SaTokenConfig saTokenConfig){
|
||||
SaTokenManager.setConfig(saTokenConfig);
|
||||
}
|
||||
|
||||
// 注入持久化Bean
|
||||
@Autowired(required = false)
|
||||
public void setDao(SaTokenDao dao){
|
||||
SaTokenManager.setDao(dao);
|
||||
}
|
||||
|
||||
// 注入权限认证Bean
|
||||
@Autowired(required = false)
|
||||
public void setStp(StpInterface stp){
|
||||
SaTokenManager.setStp(stp);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 开放权限验证接口,方便重写
|
||||
*/
|
||||
public interface StpInterface {
|
||||
|
||||
/** 返回指定login_id所拥有的权限码集合 */
|
||||
public List<Object> getPermissionCodeList(Object login_id, String login_key);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 权限验证接口 ,默认实现
|
||||
*/
|
||||
public class StpInterfaceDefaultImpl implements StpInterface {
|
||||
|
||||
@Override
|
||||
public List<Object> getPermissionCodeList(Object login_id, String login_key) {
|
||||
return new ArrayList<Object>();
|
||||
}
|
||||
|
||||
}
|
||||
306
sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpLogic.java
Normal file
306
sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpLogic.java
Normal file
@@ -0,0 +1,306 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.SaTokenUtil;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.util.SaCookieUtil;
|
||||
import cn.dev33.satoken.util.SpringMVCUtil;
|
||||
|
||||
/**
|
||||
* sa-token 权限验证,逻辑 实现类
|
||||
* <p>
|
||||
* (stp = sa-token-permission 的缩写 )
|
||||
*
|
||||
*/
|
||||
public class StpLogic {
|
||||
|
||||
|
||||
private String login_key = ""; // 持久化的key前缀,多账号体系时以此值区分,比如:login、user、admin
|
||||
|
||||
public StpLogic(String login_key) {
|
||||
this.login_key = login_key;
|
||||
}
|
||||
|
||||
// =================== 获取token 相关 ===================
|
||||
|
||||
|
||||
/** 随机生成一个tokenValue */
|
||||
public String randomTokenValue() {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
/** 获取当前tokenValue */
|
||||
public String getTokenValue(){
|
||||
// 0、获取相应对象
|
||||
HttpServletRequest request = SpringMVCUtil.getRequest();
|
||||
SaTokenConfig config = SaTokenManager.getConfig();
|
||||
String key_tokenName = getKey_tokenName();
|
||||
|
||||
// 1、尝试从request里读取
|
||||
if(request.getAttribute(SaTokenUtil.just_created_save_key) != null) {
|
||||
return String.valueOf(request.getAttribute(SaTokenUtil.just_created_save_key));
|
||||
}
|
||||
|
||||
// 2、尝试从cookie里读取
|
||||
Cookie cookie = SaCookieUtil.getCookie(request, key_tokenName);
|
||||
if(cookie != null){
|
||||
String tokenValue = cookie.getValue();
|
||||
if(tokenValue != null) {
|
||||
return tokenValue;
|
||||
}
|
||||
}
|
||||
|
||||
// 3、尝试从header力读取
|
||||
if(config.getIsReadHead() == true){
|
||||
String tokenValue = request.getHeader(key_tokenName);
|
||||
if(tokenValue != null) {
|
||||
return tokenValue;
|
||||
}
|
||||
}
|
||||
|
||||
// 4、尝试从请求体里面读取
|
||||
if(config.getIsReadBody() == true){
|
||||
String tokenValue = request.getParameter(key_tokenName);
|
||||
if(tokenValue != null) {
|
||||
return tokenValue;
|
||||
}
|
||||
}
|
||||
|
||||
// 5、都读取不到,那算了吧还是
|
||||
return null;
|
||||
}
|
||||
|
||||
/** 获取指定id的tokenValue */
|
||||
public String getTokenValueByLoginId(Object login_id) {
|
||||
return SaTokenManager.getDao().getValue(getKey_LoginId(login_id));
|
||||
}
|
||||
|
||||
/** 获取当前会话的token信息:tokenName与tokenValue */
|
||||
public Map<String, String> getTokenInfo() {
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("tokenName", getKey_tokenName());
|
||||
map.put("tokenValue", getTokenValue());
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
// =================== 登录相关操作 ===================
|
||||
|
||||
/** 在当前会话上登录id ,建议的类型:(long | int | String) */
|
||||
public void setLoginId(Object login_id) {
|
||||
|
||||
// 1、获取相应对象
|
||||
HttpServletRequest request = SpringMVCUtil.getRequest();
|
||||
SaTokenConfig config = SaTokenManager.getConfig();
|
||||
SaTokenDao dao = SaTokenManager.getDao();
|
||||
|
||||
// 2、获取tokenValue
|
||||
String tokenValue = getTokenValueByLoginId(login_id); // 获取旧tokenValue
|
||||
if(tokenValue == null){ // 为null则创建一个新的
|
||||
tokenValue = randomTokenValue();
|
||||
} else {
|
||||
// 不为null, 并且配置不共享,则删掉原来,并且创建新的
|
||||
if(config.getIsShare() == false){
|
||||
dao.delKey(getKey_TokenValue(tokenValue));
|
||||
tokenValue = randomTokenValue();
|
||||
}
|
||||
}
|
||||
|
||||
// 3、持久化
|
||||
dao.setValue(getKey_TokenValue(tokenValue), String.valueOf(login_id), config.getTimeout()); // token -> uid
|
||||
dao.setValue(getKey_LoginId(login_id), tokenValue, config.getTimeout()); // uid -> token
|
||||
request.setAttribute(SaTokenUtil.just_created_save_key, tokenValue); // 保存到本次request里
|
||||
SaCookieUtil.addCookie(SpringMVCUtil.getResponse(), getKey_tokenName(), tokenValue, "/", (int)config.getTimeout()); // cookie注入
|
||||
}
|
||||
|
||||
/** 当前会话注销登录 */
|
||||
public void logout() {
|
||||
Object login_id = getLoginId_defaultNull();
|
||||
if(login_id != null) {
|
||||
logoutByLoginId(login_id);
|
||||
SaCookieUtil.delCookie(SpringMVCUtil.getRequest(), SpringMVCUtil.getResponse(), getKey_tokenName()); // 清除cookie
|
||||
}
|
||||
}
|
||||
|
||||
/** 指定login_id的会话注销登录(踢人下线) */
|
||||
public void logoutByLoginId(Object login_id) {
|
||||
|
||||
// 获取相应tokenValue
|
||||
String tokenValue = getTokenValueByLoginId(login_id);
|
||||
if(tokenValue == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 清除相关数据
|
||||
SaTokenManager.getDao().delKey(getKey_TokenValue(tokenValue)); // 清除token-id键值对
|
||||
SaTokenManager.getDao().delKey(getKey_LoginId(login_id)); // 清除id-token键值对
|
||||
SaTokenManager.getDao().delKey(getKey_session(login_id)); // 清除其session
|
||||
// SaCookieUtil.delCookie(SpringMVCUtil.getRequest(), SpringMVCUtil.getResponse(), getKey_tokenName()); // 清除cookie
|
||||
}
|
||||
|
||||
// 查询相关
|
||||
|
||||
/** 获取当前会话是否已经登录 */
|
||||
public boolean isLogin() {
|
||||
return getLoginId_defaultNull() != null;
|
||||
}
|
||||
|
||||
/** 获取当前会话登录id, 如果未登录,则抛出异常 */
|
||||
public Object getLoginId() {
|
||||
Object login_id = getLoginId_defaultNull();
|
||||
if(login_id == null) {
|
||||
throw new NotLoginException();
|
||||
}
|
||||
return login_id;
|
||||
}
|
||||
|
||||
/** 获取当前会话登录id, 如果未登录,则返回默认值 */
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T>T getLoginId(T default_value) {
|
||||
Object login_id = getLoginId_defaultNull();
|
||||
if(login_id == null) {
|
||||
return default_value;
|
||||
}
|
||||
return (T)login_id;
|
||||
}
|
||||
|
||||
/** 获取当前会话登录id, 如果未登录,则返回null */
|
||||
public Object getLoginId_defaultNull() {
|
||||
String tokenValue = getTokenValue();
|
||||
if(tokenValue != null) {
|
||||
Object login_id = SaTokenManager.getDao().getValue(getKey_TokenValue(tokenValue));
|
||||
if(login_id != null) {
|
||||
return login_id;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** 获取当前会话登录id, 并转换为String */
|
||||
public String getLoginId_asString() {
|
||||
return String.valueOf(getLoginId());
|
||||
}
|
||||
|
||||
/** 获取当前会话登录id, 并转换为int */
|
||||
public int getLoginId_asInt() {
|
||||
// Object login_id = getLoginId();
|
||||
// if(login_id instanceof Integer) {
|
||||
// return (Integer)login_id;
|
||||
// }
|
||||
return Integer.valueOf(String.valueOf(getLoginId()));
|
||||
}
|
||||
|
||||
/** 获取当前会话登录id, 并转换为long */
|
||||
public long getLoginId_asLong() {
|
||||
// Object login_id = getLoginId();
|
||||
// if(login_id instanceof Long) {
|
||||
// return (Long)login_id;
|
||||
// }
|
||||
return Long.valueOf(String.valueOf(getLoginId()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================== session相关 ===================
|
||||
|
||||
/** 获取指定key的session, 如果没有,is_create=是否新建并返回 */
|
||||
protected SaSession getSessionBySessionId(String sessionId, boolean is_create) {
|
||||
SaSession session = SaTokenManager.getDao().getSaSession(sessionId);
|
||||
if(session == null && is_create) {
|
||||
session = new SaSession(sessionId);
|
||||
SaTokenManager.getDao().saveSaSession(session, SaTokenManager.getConfig().getTimeout());
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
/** 获取指定login_id的session */
|
||||
public SaSession getSessionByLoginId(Object login_id) {
|
||||
return getSessionBySessionId(getKey_session(login_id), false);
|
||||
}
|
||||
|
||||
/** 获取当前会话的session */
|
||||
public SaSession getSession() {
|
||||
return getSessionBySessionId(getKey_session(getLoginId()), true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================== 权限验证操作 ===================
|
||||
|
||||
/** 指定login_id是否含有指定权限 */
|
||||
public boolean hasPermission(Object login_id, Object pcode) {
|
||||
List<Object> pcodeList = SaTokenManager.getStp().getPermissionCodeList(login_id, login_key);
|
||||
return !(pcodeList == null || pcodeList.contains(pcode) == false);
|
||||
}
|
||||
|
||||
/** 当前会话是否含有指定权限 */
|
||||
public boolean hasPermission(Object pcode) {
|
||||
return hasPermission(getLoginId(), pcode);
|
||||
}
|
||||
|
||||
/** 当前账号是否含有指定权限 , 没有就抛出异常 */
|
||||
public void checkPermission(Object pcode) {
|
||||
if(hasPermission(pcode) == false) {
|
||||
throw new NotPermissionException(pcode);
|
||||
}
|
||||
}
|
||||
|
||||
/** 当前账号是否含有指定权限 , 【指定多个,必须全都有】 */
|
||||
public void checkPermissionAnd(Object... pcodeArray){
|
||||
Object login_id = getLoginId();
|
||||
List<Object> pcodeList = SaTokenManager.getStp().getPermissionCodeList(login_id, login_key);
|
||||
for (Object pcode : pcodeArray) {
|
||||
if(pcodeList.contains(pcode) == false) {
|
||||
throw new NotPermissionException(pcode); // 没有权限抛出异常
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 当前账号是否含有指定权限 , 【指定多个,有一个就可以了】 */
|
||||
public void checkPermissionOr(Object... pcodeArray){
|
||||
Object login_id = getLoginId();
|
||||
List<Object> pcodeList = SaTokenManager.getStp().getPermissionCodeList(login_id, login_key);
|
||||
for (Object pcode : pcodeArray) {
|
||||
if(pcodeList.contains(pcode) == true) {
|
||||
return; // 有的话提前退出
|
||||
}
|
||||
}
|
||||
if(pcodeArray.length > 0) {
|
||||
throw new NotPermissionException(pcodeArray[0]); // 没有权限抛出异常
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =================== 返回相应key ===================
|
||||
|
||||
/** 获取key:客户端 tokenName */
|
||||
public String getKey_tokenName() {
|
||||
return SaTokenManager.getConfig().getTokenName();
|
||||
}
|
||||
/** 获取key: tokenValue 持久化 */
|
||||
public String getKey_TokenValue(String tokenValue) {
|
||||
return SaTokenManager.getConfig().getTokenName() + ":" + login_key + ":token:" + tokenValue;
|
||||
}
|
||||
/** 获取key: id 持久化 */
|
||||
public String getKey_LoginId(Object login_id) {
|
||||
return SaTokenManager.getConfig().getTokenName() + ":" + login_key + ":id:" + login_id;
|
||||
}
|
||||
/** 获取key: session 持久化 */
|
||||
public String getKey_session(Object login_id) {
|
||||
return SaTokenManager.getConfig().getTokenName() + ":" + login_key + ":session:" + login_id;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
131
sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpUtil.java
Normal file
131
sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpUtil.java
Normal file
@@ -0,0 +1,131 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
|
||||
/**
|
||||
* 一个默认的实现
|
||||
*/
|
||||
@Service
|
||||
public class StpUtil {
|
||||
|
||||
// 底层的 StpLogic 对象
|
||||
public static StpLogic stpLogic = new StpLogic("login");
|
||||
|
||||
|
||||
// =================== 获取token 相关 ===================
|
||||
|
||||
|
||||
/** 获取当前tokenValue */
|
||||
public static String getTokenValue() {
|
||||
return stpLogic.getTokenValue();
|
||||
}
|
||||
|
||||
/** 获取指定id的tokenValue */
|
||||
public static String getTokenValueByLoginId(Object login_id) {
|
||||
return stpLogic.getTokenValueByLoginId(login_id);
|
||||
}
|
||||
|
||||
/** 获取当前会话的token信息:tokenName与tokenValue */
|
||||
public static Map<String, String> getTokenInfo() {
|
||||
return stpLogic.getTokenInfo();
|
||||
}
|
||||
|
||||
// =================== 登录相关操作 ===================
|
||||
|
||||
/** 在当前会话上设置登录id,建议的类型:(long | int | String) */
|
||||
public static void setLoginId(Object login_id) {
|
||||
stpLogic.setLoginId(login_id);
|
||||
}
|
||||
|
||||
/** 当前会话注销登录 */
|
||||
public static void logout() {
|
||||
stpLogic.logout();
|
||||
}
|
||||
|
||||
/** 指定login_id的会话注销登录(踢人下线) */
|
||||
public static void logoutByLoginId(Object login_id) {
|
||||
stpLogic.logoutByLoginId(login_id);
|
||||
}
|
||||
|
||||
// 查询相关
|
||||
|
||||
/** 获取当前会话是否已经登录 */
|
||||
public static boolean isLogin() {
|
||||
return stpLogic.isLogin();
|
||||
}
|
||||
|
||||
/** 获取当前会话登录id, 如果未登录,则抛出异常 */
|
||||
public static Object getLoginId() {
|
||||
return stpLogic.getLoginId();
|
||||
}
|
||||
|
||||
/** 获取当前会话登录id, 如果未登录,则返回默认值 */
|
||||
public static <T> T getLoginId(T default_value) {
|
||||
return stpLogic.getLoginId(default_value);
|
||||
}
|
||||
|
||||
/** 获取当前会话登录id, 如果未登录,则返回null */
|
||||
public static Object getLoginId_defaultNull() {
|
||||
return stpLogic.getLoginId_defaultNull();
|
||||
}
|
||||
|
||||
/** 获取当前会话登录id, 并转换为String */
|
||||
public static String getLoginId_asString() {
|
||||
return stpLogic.getLoginId_asString();
|
||||
}
|
||||
|
||||
/** 获取当前会话登录id, 并转换为int */
|
||||
public static int getLoginId_asInt() {
|
||||
return stpLogic.getLoginId_asInt();
|
||||
}
|
||||
|
||||
/** 获取当前会话登录id, 并转换为long */
|
||||
public static long getLoginId_asLong() {
|
||||
return stpLogic.getLoginId_asLong();
|
||||
}
|
||||
|
||||
// =================== session相关 ===================
|
||||
|
||||
/** 获取指定login_id的session */
|
||||
public static SaSession getSessionByLoginId(Object login_id) {
|
||||
return stpLogic.getSessionByLoginId(login_id);
|
||||
}
|
||||
|
||||
/** 获取当前会话的session */
|
||||
public static SaSession getSession() {
|
||||
return stpLogic.getSession();
|
||||
}
|
||||
|
||||
// =================== 权限验证操作 ===================
|
||||
|
||||
/** 指定login_id是否含有指定权限 */
|
||||
public static boolean hasPermission(Object login_id, Object pcode) {
|
||||
return stpLogic.hasPermission(login_id, pcode);
|
||||
}
|
||||
|
||||
/** 当前会话是否含有指定权限 */
|
||||
public static boolean hasPermission(Object pcode) {
|
||||
return stpLogic.hasPermission(pcode);
|
||||
}
|
||||
|
||||
/** 当前账号是否含有指定权限 , 没有就抛出异常 */
|
||||
public static void checkPermission(Object pcode) {
|
||||
stpLogic.checkPermission(pcode);
|
||||
}
|
||||
|
||||
/** 当前账号是否含有指定权限 , 【指定多个,必须全都有】 */
|
||||
public static void checkPermissionAnd(Object... pcodeArray) {
|
||||
stpLogic.checkPermissionAnd(pcodeArray);
|
||||
}
|
||||
|
||||
/** 当前账号是否含有指定权限 , 【指定多个,有一个就可以了】 */
|
||||
public static void checkPermissionOr(Object... pcodeArray) {
|
||||
stpLogic.checkPermissionOr(pcodeArray);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package cn.dev33.satoken.util;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* cookie工具类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaCookieUtil {
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定cookie
|
||||
*/
|
||||
public static Cookie getCookie(HttpServletRequest request, String cookieName) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if(cookies != null) {
|
||||
for(int i = 0; i < cookies.length; i++) {
|
||||
Cookie cookie = cookies[i];
|
||||
if(cookie != null && cookieName.equals(cookie.getName())) {
|
||||
return cookie;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加cookie
|
||||
*/
|
||||
public static void addCookie(HttpServletResponse response,String name,String value,String path,int timeout) {
|
||||
Cookie cookie = new Cookie(name, value);
|
||||
if(path == null) {
|
||||
path = "/";
|
||||
}
|
||||
cookie.setPath(path);
|
||||
cookie.setMaxAge(timeout);
|
||||
response.addCookie(cookie);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 删除cookie
|
||||
*/
|
||||
public static void delCookie(HttpServletRequest request,HttpServletResponse response,String name) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if(cookies != null){
|
||||
for(Cookie cookie : cookies) {
|
||||
if(cookies != null && (name).equals(cookie.getName())) {
|
||||
addCookie(response,name,null,null,0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 修改cookie的value值
|
||||
*/
|
||||
public static void updateCookie(HttpServletRequest request,HttpServletResponse response,String name,String value) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if(cookies != null){
|
||||
for(Cookie cookie : cookies) {
|
||||
if(cookies != null && (name).equals(cookie.getName())) {
|
||||
addCookie(response,name,value,cookie.getPath(),cookie.getMaxAge());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package cn.dev33.satoken.util;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
/**
|
||||
* SpringMVC相关操作
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SpringMVCUtil {
|
||||
|
||||
// 获取当前会话的 request
|
||||
public static HttpServletRequest getRequest() {
|
||||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();// 大善人SpringMVC提供的封装
|
||||
if(servletRequestAttributes == null) {
|
||||
throw new RuntimeException("当前环境非JavaWeb");
|
||||
}
|
||||
return servletRequestAttributes.getRequest();
|
||||
}
|
||||
|
||||
// 获取当前会话的
|
||||
public static HttpServletResponse getResponse() {
|
||||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();// 大善人SpringMVC提供的封装
|
||||
if(servletRequestAttributes == null) {
|
||||
throw new RuntimeException("当前环境非JavaWeb");
|
||||
}
|
||||
return servletRequestAttributes.getResponse();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user