mirror of
https://gitee.com/dromara/sa-token.git
synced 2024-11-29 18:37:49 +08:00
实现 OAuth2.0 模块
This commit is contained in:
parent
1df97edaf8
commit
7e0f592a0f
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,6 +6,7 @@ bin/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
*.iml
|
||||
|
||||
.factorypath
|
||||
/.factorypath
|
||||
|
1
pom.xml
1
pom.xml
@ -23,6 +23,7 @@
|
||||
<module>sa-token-dao-redis</module>
|
||||
<module>sa-token-dao-redis-jackson</module>
|
||||
<module>sa-token-spring-aop</module>
|
||||
<module>sa-token-oauth2</module>
|
||||
</modules>
|
||||
|
||||
<!-- 开源协议 apache 2.0 -->
|
||||
|
3
sa-token-core/.gitignore
vendored
3
sa-token-core/.gitignore
vendored
@ -9,4 +9,5 @@ unpackage/
|
||||
|
||||
.factorypath
|
||||
|
||||
.idea/
|
||||
.idea/
|
||||
.iml
|
@ -24,7 +24,7 @@ public interface SaTokenDao {
|
||||
* @param key 键名称
|
||||
* @return value
|
||||
*/
|
||||
public String getValue(String key);
|
||||
public String get(String key);
|
||||
|
||||
/**
|
||||
* 写入指定key-value键值对,并设定过期时间 (单位: 秒)
|
||||
@ -32,20 +32,20 @@ public interface SaTokenDao {
|
||||
* @param value 值
|
||||
* @param timeout 过期时间 (单位: 秒)
|
||||
*/
|
||||
public void setValue(String key, String value, long timeout);
|
||||
public void set(String key, String value, long timeout);
|
||||
|
||||
/**
|
||||
* 修改指定key-value键值对 (过期时间取原来的值)
|
||||
* 修改指定key-value键值对 (过期时间不变)
|
||||
* @param key 键名称
|
||||
* @param value 值
|
||||
*/
|
||||
public void updateValue(String key, String value);
|
||||
public void update(String key, String value);
|
||||
|
||||
/**
|
||||
* 删除一个指定的key
|
||||
* @param key 键名称
|
||||
*/
|
||||
public void deleteKey(String key);
|
||||
public void delete(String key);
|
||||
|
||||
/**
|
||||
* 获取指定key的剩余存活时间 (单位: 秒)
|
||||
@ -62,6 +62,51 @@ public interface SaTokenDao {
|
||||
public void updateTimeout(String key, long timeout);
|
||||
|
||||
|
||||
// --------------------- Object相关 ---------------------
|
||||
|
||||
/**
|
||||
* 根据key获取Object,如果没有,则返回空
|
||||
* @param key 键名称
|
||||
* @return object
|
||||
*/
|
||||
public Object getObject(String key);
|
||||
|
||||
/**
|
||||
* 写入指定键值对,并设定过期时间 (单位: 秒)
|
||||
* @param key 键名称
|
||||
* @param object 值
|
||||
* @param timeout 过期时间 (单位: 秒)
|
||||
*/
|
||||
public void setObject(String key, Object object, long timeout);
|
||||
|
||||
/**
|
||||
* 修改指定键值对 (过期时间不变)
|
||||
* @param key 键名称
|
||||
* @param object 值
|
||||
*/
|
||||
public void updateObject(String key, Object object);
|
||||
|
||||
/**
|
||||
* 删除一个指定的Object
|
||||
* @param key 键名称
|
||||
*/
|
||||
public void deleteObject(String key);
|
||||
|
||||
/**
|
||||
* 获取指定key的剩余存活时间 (单位: 秒)
|
||||
* @param key 指定key
|
||||
* @return 这个key的剩余存活时间
|
||||
*/
|
||||
public long getObjectTimeout(String key);
|
||||
|
||||
/**
|
||||
* 修改指定key的剩余存活时间 (单位: 秒)
|
||||
* @param key 指定key
|
||||
* @param timeout 过期时间
|
||||
*/
|
||||
public void updateObjectTimeout(String key, long timeout);
|
||||
|
||||
|
||||
// --------------------- Session相关 ---------------------
|
||||
|
||||
/**
|
||||
@ -69,40 +114,52 @@ public interface SaTokenDao {
|
||||
* @param sessionId 键名称
|
||||
* @return SaSession
|
||||
*/
|
||||
public SaSession getSession(String sessionId);
|
||||
public default SaSession getSession(String sessionId) {
|
||||
return (SaSession)getObject(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定Session持久化
|
||||
* @param session 要保存的session对象
|
||||
* @param timeout 过期时间 (单位: 秒)
|
||||
*/
|
||||
public void saveSession(SaSession session, long timeout);
|
||||
public default void setSession(SaSession session, long timeout) {
|
||||
setObject(session.getId(), session, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新指定session
|
||||
* @param session 要更新的session对象
|
||||
*/
|
||||
public void updateSession(SaSession session);
|
||||
public default void updateSession(SaSession session) {
|
||||
updateObject(session.getId(), session);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个指定的session
|
||||
* @param sessionId sessionId
|
||||
*/
|
||||
public void deleteSession(String sessionId);
|
||||
public default void deleteSession(String sessionId) {
|
||||
deleteObject(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定SaSession的剩余存活时间 (单位: 秒)
|
||||
* @param sessionId 指定SaSession
|
||||
* @return 这个SaSession的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
public long getSessionTimeout(String sessionId);
|
||||
public default long getSessionTimeout(String sessionId) {
|
||||
return getObjectTimeout(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定SaSession的剩余存活时间 (单位: 秒)
|
||||
* @param sessionId sessionId
|
||||
* @param timeout 过期时间
|
||||
*/
|
||||
public void updateSessionTimeout(String sessionId, long timeout);
|
||||
public default void updateSessionTimeout(String sessionId, long timeout) {
|
||||
updateObjectTimeout(sessionId, timeout);
|
||||
}
|
||||
|
||||
|
||||
// --------------------- 会话管理 ---------------------
|
||||
|
@ -8,7 +8,6 @@ import java.util.Timer;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.util.SaTaskUtil;
|
||||
import cn.dev33.satoken.util.SaTaskUtil.FunctionRunClass;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
@ -42,19 +41,19 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao {
|
||||
// ------------------------ String 读写操作
|
||||
|
||||
@Override
|
||||
public String getValue(String key) {
|
||||
public String get(String key) {
|
||||
clearKeyByTimeout(key);
|
||||
return (String)dataMap.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String key, String value, long timeout) {
|
||||
public void set(String key, String value, long timeout) {
|
||||
dataMap.put(key, value);
|
||||
expireMap.put(key, (timeout == SaTokenDao.NEVER_EXPIRE) ? (SaTokenDao.NEVER_EXPIRE) : (System.currentTimeMillis() + timeout * 1000));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateValue(String key, String value) {
|
||||
public void update(String key, String value) {
|
||||
if(getKeyTimeout(key) == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
@ -62,7 +61,7 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteKey(String key) {
|
||||
public void delete(String key) {
|
||||
dataMap.remove(key);
|
||||
expireMap.remove(key);
|
||||
}
|
||||
@ -76,45 +75,49 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao {
|
||||
public void updateTimeout(String key, long timeout) {
|
||||
expireMap.put(key, System.currentTimeMillis() + timeout * 1000);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ------------------------ Session 读写操作
|
||||
// ------------------------ Object 读写操作
|
||||
|
||||
@Override
|
||||
public SaSession getSession(String sessionId) {
|
||||
clearKeyByTimeout(sessionId);
|
||||
return (SaSession)dataMap.get(sessionId);
|
||||
public Object getObject(String key) {
|
||||
clearKeyByTimeout(key);
|
||||
return dataMap.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveSession(SaSession session, long timeout) {
|
||||
dataMap.put(session.getId(), session);
|
||||
expireMap.put(session.getId(), (timeout == SaTokenDao.NEVER_EXPIRE) ? (SaTokenDao.NEVER_EXPIRE) : (System.currentTimeMillis() + timeout * 1000));
|
||||
public void setObject(String key, Object object, long timeout) {
|
||||
dataMap.put(key, object);
|
||||
expireMap.put(key, (timeout == SaTokenDao.NEVER_EXPIRE) ? (SaTokenDao.NEVER_EXPIRE) : (System.currentTimeMillis() + timeout * 1000));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSession(SaSession session) {
|
||||
if(getKeyTimeout(session.getId()) == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
public void updateObject(String key, Object object) {
|
||||
if(getKeyTimeout(key) == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
// 无动作
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSession(String sessionId) {
|
||||
dataMap.remove(sessionId);
|
||||
expireMap.remove(sessionId);
|
||||
public void deleteObject(String key) {
|
||||
dataMap.remove(key);
|
||||
expireMap.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getObjectTimeout(String key) {
|
||||
return getKeyTimeout(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateObjectTimeout(String key, long timeout) {
|
||||
expireMap.put(key, System.currentTimeMillis() + timeout * 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSessionTimeout(String sessionId) {
|
||||
return getKeyTimeout(sessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSessionTimeout(String sessionId, long timeout) {
|
||||
expireMap.put(sessionId, System.currentTimeMillis() + timeout * 1000);
|
||||
}
|
||||
// ------------------------ Session 读写操作
|
||||
// 使用接口默认实现
|
||||
|
||||
|
||||
// ------------------------ 过期时间相关操作
|
||||
@ -215,6 +218,8 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao {
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size) {
|
||||
return SaTokenInsideUtil.searchList(expireMap.keySet(), prefix, keyword, start, size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -32,4 +32,14 @@ public class SaTokenException extends RuntimeException {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一个异常
|
||||
*
|
||||
* @param message 异常信息
|
||||
* @param cause 异常对象
|
||||
*/
|
||||
public SaTokenException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ public class SaSessionCustomUtil {
|
||||
SaSession session = SaTokenManager.getSaTokenDao().getSession(getSessionKey(sessionId));
|
||||
if (session == null && isCreate) {
|
||||
session = SaTokenManager.getSaTokenAction().createSession(sessionId);
|
||||
SaTokenManager.getSaTokenDao().saveSession(session, SaTokenManager.getConfig().getTimeout());
|
||||
SaTokenManager.getSaTokenDao().setSession(session, SaTokenManager.getConfig().getTimeout());
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ public class StpLogic {
|
||||
for (TokenSign tokenSign : tokenSignList) {
|
||||
if(tokenSign.getDevice().equals(device)) {
|
||||
// 1. 将此token 标记为已顶替
|
||||
dao.updateValue(getKeyTokenValue(tokenSign.getValue()), NotLoginException.BE_REPLACED);
|
||||
dao.update(getKeyTokenValue(tokenSign.getValue()), NotLoginException.BE_REPLACED);
|
||||
// 2. 清理掉[token-最后操作时间]
|
||||
clearLastActivity(tokenSign.getValue());
|
||||
// 3. 清理账号session上的token签名记录
|
||||
@ -203,7 +203,7 @@ public class StpLogic {
|
||||
|
||||
// ------ 4. 持久化其它数据
|
||||
// token -> uid
|
||||
dao.setValue(getKeyTokenValue(tokenValue), String.valueOf(loginId), config.getTimeout());
|
||||
dao.set(getKeyTokenValue(tokenValue), String.valueOf(loginId), config.getTimeout());
|
||||
// 将token保存到本次request里
|
||||
request.setAttribute(getKeyJustCreatedSave(), tokenValue);
|
||||
// 写入 [最后操作时间]
|
||||
@ -244,7 +244,7 @@ public class StpLogic {
|
||||
if(loginId == null || NotLoginException.ABNORMAL_LIST.contains(loginId)) {
|
||||
return;
|
||||
}
|
||||
SaTokenManager.getSaTokenDao().deleteKey(getKeyTokenValue(tokenValue));
|
||||
SaTokenManager.getSaTokenDao().delete(getKeyTokenValue(tokenValue));
|
||||
|
||||
// 3. 尝试清理账号session上的token签名 (如果为null或已被标记为异常, 那么无需继续执行 )
|
||||
SaSession session = getSessionByLoginId(loginId, false);
|
||||
@ -288,7 +288,7 @@ public class StpLogic {
|
||||
// 2. 清理掉[token-最后操作时间]
|
||||
clearLastActivity(tokenValue);
|
||||
// 3. 标记:已被踢下线
|
||||
SaTokenManager.getSaTokenDao().updateValue(getKeyTokenValue(tokenValue), NotLoginException.KICK_OUT);
|
||||
SaTokenManager.getSaTokenDao().update(getKeyTokenValue(tokenValue), NotLoginException.KICK_OUT);
|
||||
// 4. 清理账号session上的token签名
|
||||
session.removeTokenSign(tokenValue);
|
||||
}
|
||||
@ -450,7 +450,7 @@ public class StpLogic {
|
||||
* @return loginId
|
||||
*/
|
||||
public String getLoginIdNotHandle(String tokenValue) {
|
||||
return SaTokenManager.getSaTokenDao().getValue(getKeyTokenValue(tokenValue));
|
||||
return SaTokenManager.getSaTokenDao().get(getKeyTokenValue(tokenValue));
|
||||
}
|
||||
|
||||
|
||||
@ -466,7 +466,7 @@ public class StpLogic {
|
||||
SaSession session = SaTokenManager.getSaTokenDao().getSession(sessionId);
|
||||
if(session == null && isCreate) {
|
||||
session = SaTokenManager.getSaTokenAction().createSession(sessionId);
|
||||
SaTokenManager.getSaTokenDao().saveSession(session, getConfig().getTimeout());
|
||||
SaTokenManager.getSaTokenDao().setSession(session, getConfig().getTimeout());
|
||||
}
|
||||
return session;
|
||||
}
|
||||
@ -589,7 +589,7 @@ public class StpLogic {
|
||||
return;
|
||||
}
|
||||
// 将[最后操作时间]标记为当前时间戳
|
||||
SaTokenManager.getSaTokenDao().setValue(getKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis()), getConfig().getTimeout());
|
||||
SaTokenManager.getSaTokenDao().set(getKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis()), getConfig().getTimeout());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -602,7 +602,7 @@ public class StpLogic {
|
||||
return;
|
||||
}
|
||||
// 删除[最后操作时间]
|
||||
SaTokenManager.getSaTokenDao().deleteKey(getKeyLastActivityTime(tokenValue));
|
||||
SaTokenManager.getSaTokenDao().delete(getKeyLastActivityTime(tokenValue));
|
||||
// 清除标记
|
||||
SaTokenManager.getSaTokenServlet().getRequest().removeAttribute(SaTokenConsts.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY);
|
||||
}
|
||||
@ -654,7 +654,7 @@ public class StpLogic {
|
||||
if(tokenValue == null || getConfig().getActivityTimeout() == SaTokenDao.NEVER_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
SaTokenManager.getSaTokenDao().updateValue(getKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis()));
|
||||
SaTokenManager.getSaTokenDao().update(getKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -745,7 +745,7 @@ public class StpLogic {
|
||||
// ------ 开始查询
|
||||
// 获取相关数据
|
||||
String keyLastActivityTime = getKeyLastActivityTime(tokenValue);
|
||||
String lastActivityTimeString = SaTokenManager.getSaTokenDao().getValue(keyLastActivityTime);
|
||||
String lastActivityTimeString = SaTokenManager.getSaTokenDao().get(keyLastActivityTime);
|
||||
// 查不到,返回-2
|
||||
if(lastActivityTimeString == null) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
|
3
sa-token-dao-redis-jackson/.gitignore
vendored
3
sa-token-dao-redis-jackson/.gitignore
vendored
@ -9,4 +9,5 @@ unpackage/
|
||||
|
||||
.factorypath
|
||||
|
||||
.idea/
|
||||
.idea/
|
||||
.iml
|
@ -17,7 +17,6 @@ import org.springframework.stereotype.Component;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
@ -35,17 +34,17 @@ public class SaTokenDaoRedisJackson implements SaTokenDao {
|
||||
public ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* string专用
|
||||
* String专用
|
||||
*/
|
||||
@Autowired
|
||||
public StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
/**
|
||||
* SaSession专用
|
||||
* Object专用
|
||||
*/
|
||||
public RedisTemplate<String, SaSession> sessionRedisTemplate;
|
||||
public RedisTemplate<String, Object> objectRedisTemplate;
|
||||
@Autowired
|
||||
public void setSessionRedisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
public void setObjectRedisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
// 指定相应的序列化方案
|
||||
StringRedisSerializer keySerializer = new StringRedisSerializer();
|
||||
GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer();
|
||||
@ -60,15 +59,15 @@ public class SaTokenDaoRedisJackson implements SaTokenDao {
|
||||
System.err.println(e.getMessage());
|
||||
}
|
||||
// 构建RedisTemplate
|
||||
RedisTemplate<String, SaSession> template = new RedisTemplate<String, SaSession>();
|
||||
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.sessionRedisTemplate == null) {
|
||||
this.sessionRedisTemplate = template;
|
||||
if(this.objectRedisTemplate == null) {
|
||||
this.objectRedisTemplate = template;
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,7 +76,7 @@ public class SaTokenDaoRedisJackson implements SaTokenDao {
|
||||
* 根据key获取value,如果没有,则返回空
|
||||
*/
|
||||
@Override
|
||||
public String getValue(String key) {
|
||||
public String get(String key) {
|
||||
return stringRedisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
@ -85,7 +84,7 @@ public class SaTokenDaoRedisJackson implements SaTokenDao {
|
||||
* 写入指定key-value键值对,并设定过期时间(单位:秒)
|
||||
*/
|
||||
@Override
|
||||
public void setValue(String key, String value, long timeout) {
|
||||
public void set(String key, String value, long timeout) {
|
||||
// 判断是否为永不过期
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
stringRedisTemplate.opsForValue().set(key, value);
|
||||
@ -95,23 +94,23 @@ public class SaTokenDaoRedisJackson implements SaTokenDao {
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定key-value键值对 (过期时间取原来的值)
|
||||
* 修改指定key-value键值对 (过期时间不变)
|
||||
*/
|
||||
@Override
|
||||
public void updateValue(String key, String value) {
|
||||
public void update(String key, String value) {
|
||||
long expire = getTimeout(key);
|
||||
// -2 = 无此键
|
||||
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
this.setValue(key, value, expire);
|
||||
this.set(key, value, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个指定的key
|
||||
*/
|
||||
@Override
|
||||
public void deleteKey(String key) {
|
||||
public void delete(String key) {
|
||||
stringRedisTemplate.delete(key);
|
||||
}
|
||||
|
||||
@ -135,7 +134,7 @@ public class SaTokenDaoRedisJackson implements SaTokenDao {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.setValue(key, this.getValue(key), timeout);
|
||||
this.set(key, this.get(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -143,76 +142,77 @@ public class SaTokenDaoRedisJackson implements SaTokenDao {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据指定key的Session,如果没有,则返回空
|
||||
* 根据key获取Object,如果没有,则返回空
|
||||
*/
|
||||
@Override
|
||||
public SaSession getSession(String sessionId) {
|
||||
return sessionRedisTemplate.opsForValue().get(sessionId);
|
||||
public Object getObject(String key) {
|
||||
return objectRedisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定Session持久化
|
||||
* 写入指定键值对,并设定过期时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void saveSession(SaSession session, long timeout) {
|
||||
public void setObject(String key, Object object, long timeout) {
|
||||
// 判断是否为永不过期
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
sessionRedisTemplate.opsForValue().set(session.getId(), session);
|
||||
objectRedisTemplate.opsForValue().set(key, object);
|
||||
} else {
|
||||
sessionRedisTemplate.opsForValue().set(session.getId(), session, timeout, TimeUnit.SECONDS);
|
||||
objectRedisTemplate.opsForValue().set(key, object, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新指定session
|
||||
* 修改指定键值对 (过期时间不变)
|
||||
*/
|
||||
@Override
|
||||
public void updateSession(SaSession session) {
|
||||
long expire = getSessionTimeout(session.getId());
|
||||
public void updateObject(String key, Object object) {
|
||||
long expire = getObjectTimeout(key);
|
||||
// -2 = 无此键
|
||||
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
this.saveSession(session, expire);
|
||||
this.setObject(key, object, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个指定的session
|
||||
* 删除一个指定的object
|
||||
*/
|
||||
@Override
|
||||
public void deleteSession(String sessionId) {
|
||||
sessionRedisTemplate.delete(sessionId);
|
||||
public void deleteObject(String key) {
|
||||
objectRedisTemplate.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定SaSession的剩余存活时间 (单位: 秒)
|
||||
* 获取指定key的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public long getSessionTimeout(String sessionId) {
|
||||
return sessionRedisTemplate.getExpire(sessionId);
|
||||
public long getObjectTimeout(String key) {
|
||||
return objectRedisTemplate.getExpire(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定SaSession的剩余存活时间 (单位: 秒)
|
||||
* 修改指定key的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void updateSessionTimeout(String sessionId, long timeout) {
|
||||
public void updateObjectTimeout(String key, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
long expire = getSessionTimeout(sessionId);
|
||||
long expire = getObjectTimeout(key);
|
||||
if(expire == SaTokenDao.NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.saveSession(this.getSession(sessionId), timeout);
|
||||
this.setObject(key, this.getObject(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
sessionRedisTemplate.expire(sessionId, timeout, TimeUnit.SECONDS);
|
||||
objectRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 搜索数据
|
||||
*/
|
||||
|
3
sa-token-dao-redis/.gitignore
vendored
3
sa-token-dao-redis/.gitignore
vendored
@ -9,4 +9,5 @@ unpackage/
|
||||
|
||||
.factorypath
|
||||
|
||||
.idea/
|
||||
.idea/
|
||||
.iml
|
@ -13,7 +13,6 @@ import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
@ -26,30 +25,30 @@ import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
public class SaTokenDaoRedis implements SaTokenDao {
|
||||
|
||||
/**
|
||||
* string专用
|
||||
* String专用
|
||||
*/
|
||||
@Autowired
|
||||
public StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
/**
|
||||
* SaSession专用
|
||||
* Objecy专用
|
||||
*/
|
||||
public RedisTemplate<String, SaSession> sessionRedisTemplate;
|
||||
public RedisTemplate<String, Object> objectRedisTemplate;
|
||||
@Autowired
|
||||
public void setSessionRedisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
public void setObjectRedisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
// 指定相应的序列化方案
|
||||
StringRedisSerializer keySerializer = new StringRedisSerializer();
|
||||
JdkSerializationRedisSerializer valueSerializer = new JdkSerializationRedisSerializer();
|
||||
// 构建RedisTemplate
|
||||
RedisTemplate<String, SaSession> template = new RedisTemplate<String, SaSession>();
|
||||
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.sessionRedisTemplate == null) {
|
||||
this.sessionRedisTemplate = template;
|
||||
if(this.objectRedisTemplate == null) {
|
||||
this.objectRedisTemplate = template;
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,7 +57,7 @@ public class SaTokenDaoRedis implements SaTokenDao {
|
||||
* 根据key获取value,如果没有,则返回空
|
||||
*/
|
||||
@Override
|
||||
public String getValue(String key) {
|
||||
public String get(String key) {
|
||||
return stringRedisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
@ -66,7 +65,7 @@ public class SaTokenDaoRedis implements SaTokenDao {
|
||||
* 写入指定key-value键值对,并设定过期时间(单位:秒)
|
||||
*/
|
||||
@Override
|
||||
public void setValue(String key, String value, long timeout) {
|
||||
public void set(String key, String value, long timeout) {
|
||||
// 判断是否为永不过期
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
stringRedisTemplate.opsForValue().set(key, value);
|
||||
@ -76,23 +75,23 @@ public class SaTokenDaoRedis implements SaTokenDao {
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定key-value键值对 (过期时间取原来的值)
|
||||
* 修改指定key-value键值对 (过期时间不变)
|
||||
*/
|
||||
@Override
|
||||
public void updateValue(String key, String value) {
|
||||
public void update(String key, String value) {
|
||||
long expire = getTimeout(key);
|
||||
// -2 = 无此键
|
||||
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
this.setValue(key, value, expire);
|
||||
this.set(key, value, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个指定的key
|
||||
*/
|
||||
@Override
|
||||
public void deleteKey(String key) {
|
||||
public void delete(String key) {
|
||||
stringRedisTemplate.delete(key);
|
||||
}
|
||||
|
||||
@ -116,7 +115,7 @@ public class SaTokenDaoRedis implements SaTokenDao {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.setValue(key, this.getValue(key), timeout);
|
||||
this.set(key, this.get(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -124,74 +123,73 @@ public class SaTokenDaoRedis implements SaTokenDao {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据指定key的Session,如果没有,则返回空
|
||||
* 根据key获取Object,如果没有,则返回空
|
||||
*/
|
||||
@Override
|
||||
public SaSession getSession(String sessionId) {
|
||||
return sessionRedisTemplate.opsForValue().get(sessionId);
|
||||
public Object getObject(String key) {
|
||||
return objectRedisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定Session持久化
|
||||
* 写入指定键值对,并设定过期时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void saveSession(SaSession session, long timeout) {
|
||||
public void setObject(String key, Object object, long timeout) {
|
||||
// 判断是否为永不过期
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
sessionRedisTemplate.opsForValue().set(session.getId(), session);
|
||||
objectRedisTemplate.opsForValue().set(key, object);
|
||||
} else {
|
||||
sessionRedisTemplate.opsForValue().set(session.getId(), session, timeout, TimeUnit.SECONDS);
|
||||
objectRedisTemplate.opsForValue().set(key, object, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新指定session
|
||||
* 修改指定键值对 (过期时间不变)
|
||||
*/
|
||||
@Override
|
||||
public void updateSession(SaSession session) {
|
||||
long expire = getSessionTimeout(session.getId());
|
||||
public void updateObject(String key, Object object) {
|
||||
long expire = getObjectTimeout(key);
|
||||
// -2 = 无此键
|
||||
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
this.saveSession(session, expire);
|
||||
this.setObject(key, object, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个指定的session
|
||||
* 删除一个指定的object
|
||||
*/
|
||||
@Override
|
||||
public void deleteSession(String sessionId) {
|
||||
sessionRedisTemplate.delete(sessionId);
|
||||
public void deleteObject(String key) {
|
||||
objectRedisTemplate.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定SaSession的剩余存活时间 (单位: 秒)
|
||||
* 获取指定key的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public long getSessionTimeout(String sessionId) {
|
||||
return sessionRedisTemplate.getExpire(sessionId);
|
||||
public long getObjectTimeout(String key) {
|
||||
return objectRedisTemplate.getExpire(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定SaSession的剩余存活时间 (单位: 秒)
|
||||
* 修改指定key的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void updateSessionTimeout(String sessionId, long timeout) {
|
||||
public void updateObjectTimeout(String key, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
long expire = getSessionTimeout(sessionId);
|
||||
long expire = getObjectTimeout(key);
|
||||
if(expire == SaTokenDao.NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.saveSession(this.getSession(sessionId), timeout);
|
||||
this.setObject(key, this.getObject(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
sessionRedisTemplate.expire(sessionId, timeout, TimeUnit.SECONDS);
|
||||
objectRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
|
13
sa-token-demo-oauth2/sa-token-demo-oauth2-client/.gitignore
vendored
Normal file
13
sa-token-demo-oauth2/sa-token-demo-oauth2-client/.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
target/
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
|
||||
/.idea/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
/.apt_generated/
|
||||
/.apt_generated_tests/
|
54
sa-token-demo-oauth2/sa-token-demo-oauth2-client/pom.xml
Normal file
54
sa-token-demo-oauth2/sa-token-demo-oauth2-client/pom.xml
Normal file
@ -0,0 +1,54 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.pj</groupId>
|
||||
<artifactId>sa-token-demo-oauth2-client</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.3.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<!-- 指定一些属性 -->
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- springboot依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.13.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- OkHttps网络请求库: http://okhttps.ejlchina.com/ -->
|
||||
<dependency>
|
||||
<groupId>com.ejlchina</groupId>
|
||||
<artifactId>okhttps</artifactId>
|
||||
<version>2.4.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- ConfigurationProperties -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
</project>
|
@ -0,0 +1,18 @@
|
||||
package com.pj;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 启动
|
||||
* @author kong
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class SaOAuth2ClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaOAuth2ClientApplication.class, args);
|
||||
System.out.println("\n客户端启动成功,访问: http://localhost:8002/login.html");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package com.pj.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.ejlchina.okhttps.OkHttps;
|
||||
import com.pj.utils.AjaxJson;
|
||||
import com.pj.utils.SoMap;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 登录注册Controller
|
||||
* @author kong
|
||||
*/
|
||||
@RestController
|
||||
public class ClientAccController {
|
||||
|
||||
|
||||
// 返回当前登录者的账号id, 如果未登录, 返回null
|
||||
@RequestMapping("/getLoginInfo")
|
||||
public AjaxJson getLoginInfo() {
|
||||
Object loginId = StpUtil.getLoginIdDefaultNull();
|
||||
return AjaxJson.getSuccessData(loginId);
|
||||
}
|
||||
|
||||
// 注销登录
|
||||
@RequestMapping("/logout")
|
||||
public AjaxJson logout() {
|
||||
StpUtil.logout();
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 根据code码进行登录
|
||||
@RequestMapping("/doCodeLogin")
|
||||
public AjaxJson doCodeLogin(String code) {
|
||||
System.out.println("------------------ 成功进入请求 ------------------");
|
||||
|
||||
// 请求服务提供方接口地址,获取 access_token 以及其他信息
|
||||
// 携带三个关键参数: code、client_id、client_secret
|
||||
String str = OkHttps.sync("http://localhost:8001/oauth2/getAccessToken")
|
||||
.addBodyPara("code", code)
|
||||
.addBodyPara("client_id", "123123123")
|
||||
.addBodyPara("client_secret", "aaaa-bbbb-cccc-dddd-eeee")
|
||||
.post()
|
||||
.getBody()
|
||||
.toString();
|
||||
SoMap so = SoMap.getSoMap().setJsonString(str);
|
||||
System.out.println("返回结果: " + so);
|
||||
|
||||
// code不等于200 代表请求失败
|
||||
if(so.getInt("code") != 200) {
|
||||
return AjaxJson.getError(so.getString("msg"));
|
||||
}
|
||||
|
||||
// 根据openid获取其对应的userId
|
||||
String openid = so.getString("openid");
|
||||
long userId = getUserIdByOpenid(openid);
|
||||
|
||||
// 登录并返回账号信息
|
||||
StpUtil.setLoginId(userId);
|
||||
return AjaxJson.getSuccessData(userId).set("openid", openid);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ------------ 模拟方法 ------------------
|
||||
|
||||
// 模拟方法:根据openid获取userId
|
||||
private long getUserIdByOpenid(String openid) {
|
||||
// 此方法仅做模拟,实际开发要根据具体业务逻辑来获取userId
|
||||
return 10001;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package com.pj.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import com.pj.utils.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
|
||||
/**
|
||||
* 全局异常拦截
|
||||
* <p> @ControllerAdvice 可指定包前缀,例如:(basePackages = "com.pj.controller.admin")
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@ControllerAdvice
|
||||
public class ExceptionHandle {
|
||||
|
||||
|
||||
/** 全局异常拦截 */
|
||||
@ResponseBody
|
||||
@ExceptionHandler
|
||||
public AjaxJson handlerException(Exception e) {
|
||||
|
||||
// 打印堆栈,以供调试
|
||||
e.printStackTrace();
|
||||
|
||||
// 记录日志信息
|
||||
AjaxJson aj = null;
|
||||
|
||||
// ------------- 判断异常类型,提供个性化提示信息
|
||||
|
||||
// 如果是未登录异常
|
||||
if(e instanceof NotLoginException){
|
||||
aj = AjaxJson.getNotLogin();
|
||||
}
|
||||
// 如果是角色异常
|
||||
else if(e instanceof NotRoleException) {
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此角色:" + ee.getCode());
|
||||
}
|
||||
// 如果是权限异常
|
||||
else if(e instanceof NotPermissionException) {
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getCode());
|
||||
}
|
||||
// 普通异常输出:500 + 异常信息
|
||||
else {
|
||||
aj = AjaxJson.getError(e.getMessage());
|
||||
}
|
||||
|
||||
// 返回到前台
|
||||
return aj;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,223 @@
|
||||
package com.pj.utils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* ajax请求返回Json格式数据的封装 <br>
|
||||
* 所有预留字段:<br>
|
||||
* code=状态码 <br>
|
||||
* msg=描述信息 <br>
|
||||
* data=携带对象 <br>
|
||||
* pageNo=当前页 <br>
|
||||
* pageSize=页大小 <br>
|
||||
* startIndex=起始索引 <br>
|
||||
* dataCount=数据总数 <br>
|
||||
* pageCount=分页总数 <br>
|
||||
* <p> 返回范例:</p>
|
||||
* <pre>
|
||||
{
|
||||
"code": 200, // 成功时=200, 失败时=500 msg=失败原因
|
||||
"msg": "ok",
|
||||
"data": {}
|
||||
}
|
||||
</pre>
|
||||
*/
|
||||
public class AjaxJson extends LinkedHashMap<String, Object> implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 1L; // 序列化版本号
|
||||
|
||||
public static final int CODE_SUCCESS = 200; // 成功状态码
|
||||
public static final int CODE_ERROR = 500; // 错误状态码
|
||||
public static final int CODE_WARNING = 501; // 警告状态码
|
||||
public static final int CODE_NOT_JUR = 403; // 无权限状态码
|
||||
public static final int CODE_NOT_LOGIN = 401; // 未登录状态码
|
||||
public static final int CODE_INVALID_REQUEST = 400; // 无效请求状态码
|
||||
|
||||
|
||||
|
||||
// ============================ 写值取值 ==================================
|
||||
|
||||
/** 给code赋值,连缀风格 */
|
||||
public AjaxJson setCode(int code) {
|
||||
this.put("code", code);
|
||||
return this;
|
||||
}
|
||||
/** 返回code */
|
||||
public Integer getCode() {
|
||||
return (Integer)this.get("code");
|
||||
}
|
||||
|
||||
/** 给msg赋值,连缀风格 */
|
||||
public AjaxJson setMsg(String msg) {
|
||||
this.put("msg", msg);
|
||||
return this;
|
||||
}
|
||||
/** 获取msg */
|
||||
public String getMsg() {
|
||||
return (String)this.get("msg");
|
||||
}
|
||||
|
||||
/** 给data赋值,连缀风格 */
|
||||
public AjaxJson setData(Object data) {
|
||||
this.put("data", data);
|
||||
return this;
|
||||
}
|
||||
/** 获取data */
|
||||
public String getData() {
|
||||
return (String)this.get("data");
|
||||
}
|
||||
/** 将data还原为指定类型并返回 */
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getData(Class<T> cs) {
|
||||
return (T) this.getData();
|
||||
}
|
||||
|
||||
/** 给dataCount(数据总数)赋值,连缀风格 */
|
||||
public AjaxJson setDataCount(Long dataCount) {
|
||||
this.put("dataCount", dataCount);
|
||||
// 如果提供了数据总数,则尝试计算page信息
|
||||
if(dataCount != null && dataCount >= 0) {
|
||||
// 如果:已有page信息
|
||||
if(get("pageNo") != null) {
|
||||
this.initPageInfo();
|
||||
}
|
||||
// // 或者:是JavaWeb环境
|
||||
// else if(SoMap.isJavaWeb()) {
|
||||
// SoMap so = SoMap.getRequestSoMap();
|
||||
// this.setPageNoAndSize(so.getKeyPageNo(), so.getKeyPageSize());
|
||||
// this.initPageInfo();
|
||||
// }
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 获取dataCount(数据总数) */
|
||||
public String getDataCount() {
|
||||
return (String)this.get("dataCount");
|
||||
}
|
||||
|
||||
/** 设置pageNo 和 pageSize,并计算出startIndex于pageCount */
|
||||
public AjaxJson setPageNoAndSize(long pageNo, long pageSize) {
|
||||
this.put("pageNo", pageNo);
|
||||
this.put("pageSize", pageSize);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 根据 pageSize dataCount,计算startIndex 与 pageCount */
|
||||
public AjaxJson initPageInfo() {
|
||||
long pageNo = (long)this.get("pageNo");
|
||||
long pageSize = (long)this.get("pageSize");
|
||||
long dataCount = (long)this.get("dataCount");
|
||||
this.set("startIndex", (pageNo - 1) * pageSize);
|
||||
long pc = dataCount / pageSize;
|
||||
this.set("pageCount", (dataCount % pageSize == 0 ? pc : pc + 1));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/** 写入一个值 自定义key, 连缀风格 */
|
||||
public AjaxJson set(String key, Object data) {
|
||||
this.put(key, data);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 写入一个Map, 连缀风格 */
|
||||
public AjaxJson setMap(Map<String, ?> map) {
|
||||
for (String key : map.keySet()) {
|
||||
this.put(key, map.get(key));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ============================ 构建 ==================================
|
||||
|
||||
public AjaxJson(int code, String msg, Object data, Long dataCount) {
|
||||
this.setCode(code);
|
||||
this.setMsg(msg);
|
||||
this.setData(data);
|
||||
this.setDataCount(dataCount == null ? -1 : dataCount);
|
||||
}
|
||||
|
||||
/** 返回成功 */
|
||||
public static AjaxJson getSuccess() {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg, Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, data, null);
|
||||
}
|
||||
public static AjaxJson getSuccessData(Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||
}
|
||||
|
||||
|
||||
/** 返回失败 */
|
||||
public static AjaxJson getError() {
|
||||
return new AjaxJson(CODE_ERROR, "error", null, null);
|
||||
}
|
||||
public static AjaxJson getError(String msg) {
|
||||
return new AjaxJson(CODE_ERROR, msg, null, null);
|
||||
}
|
||||
|
||||
/** 返回警告 */
|
||||
public static AjaxJson getWarning() {
|
||||
return new AjaxJson(CODE_ERROR, "warning", null, null);
|
||||
}
|
||||
public static AjaxJson getWarning(String msg) {
|
||||
return new AjaxJson(CODE_WARNING, msg, null, null);
|
||||
}
|
||||
|
||||
/** 返回未登录 */
|
||||
public static AjaxJson getNotLogin() {
|
||||
return new AjaxJson(CODE_NOT_LOGIN, "未登录,请登录后再次访问", null, null);
|
||||
}
|
||||
|
||||
/** 返回没有权限的 */
|
||||
public static AjaxJson getNotJur(String msg) {
|
||||
return new AjaxJson(CODE_NOT_JUR, msg, null, null);
|
||||
}
|
||||
|
||||
/** 返回一个自定义状态码的 */
|
||||
public static AjaxJson get(int code, String msg){
|
||||
return new AjaxJson(code, msg, null, null);
|
||||
}
|
||||
|
||||
/** 返回分页和数据的 */
|
||||
public static AjaxJson getPageData(Long dataCount, Object data){
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, dataCount);
|
||||
}
|
||||
|
||||
/** 返回, 根据受影响行数的(大于0=ok,小于0=error) */
|
||||
public static AjaxJson getByLine(int line){
|
||||
if(line > 0){
|
||||
return getSuccess("ok", line);
|
||||
}
|
||||
return getError("error").setData(line);
|
||||
}
|
||||
|
||||
/** 返回,根据布尔值来确定最终结果的 (true=ok,false=error) */
|
||||
public static AjaxJson getByBoolean(boolean b){
|
||||
return b ? getSuccess("ok") : getError("error");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// // 历史版本遗留代码
|
||||
// public int code; // 状态码
|
||||
// public String msg; // 描述信息
|
||||
// public Object data; // 携带对象
|
||||
// public Long dataCount; // 数据总数,用于分页
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,723 @@
|
||||
package com.pj.utils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Map< String, Object> 是最常用的一种Map类型,但是它写着麻烦
|
||||
* <p>所以特封装此类,继承Map,进行一些扩展,可以让Map更灵活使用
|
||||
* <p>最新:2020-12-10 新增部分构造方法
|
||||
* @author kong
|
||||
*/
|
||||
public class SoMap extends LinkedHashMap<String, Object> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public SoMap() {
|
||||
}
|
||||
|
||||
|
||||
/** 以下元素会在isNull函数中被判定为Null, */
|
||||
public static final Object[] NULL_ELEMENT_ARRAY = {null, ""};
|
||||
public static final List<Object> NULL_ELEMENT_LIST;
|
||||
|
||||
|
||||
static {
|
||||
NULL_ELEMENT_LIST = Arrays.asList(NULL_ELEMENT_ARRAY);
|
||||
}
|
||||
|
||||
// ============================= 读值 =============================
|
||||
|
||||
/** 获取一个值 */
|
||||
@Override
|
||||
public Object get(Object key) {
|
||||
if("this".equals(key)) {
|
||||
return this;
|
||||
}
|
||||
return super.get(key);
|
||||
}
|
||||
|
||||
/** 如果为空,则返回默认值 */
|
||||
public Object get(Object key, Object defaultValue) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/** 转为String并返回 */
|
||||
public String getString(String key) {
|
||||
Object value = get(key);
|
||||
if(value == null) {
|
||||
return null;
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
/** 如果为空,则返回默认值 */
|
||||
public String getString(String key, String defaultValue) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
/** 转为int并返回 */
|
||||
public int getInt(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return 0;
|
||||
}
|
||||
return Integer.valueOf(String.valueOf(value));
|
||||
}
|
||||
/** 转为int并返回,同时指定默认值 */
|
||||
public int getInt(String key, int defaultValue) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Integer.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为long并返回 */
|
||||
public long getLong(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return 0;
|
||||
}
|
||||
return Long.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为double并返回 */
|
||||
public double getDouble(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return 0.0;
|
||||
}
|
||||
return Double.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为boolean并返回 */
|
||||
public boolean getBoolean(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return false;
|
||||
}
|
||||
return Boolean.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为Date并返回,根据自定义格式 */
|
||||
public Date getDateByFormat(String key, String format) {
|
||||
try {
|
||||
return new SimpleDateFormat(format).parse(getString(key));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** 转为Date并返回,根据格式: yyyy-MM-dd */
|
||||
public Date getDate(String key) {
|
||||
return getDateByFormat(key, "yyyy-MM-dd");
|
||||
}
|
||||
|
||||
/** 转为Date并返回,根据格式: yyyy-MM-dd HH:mm:ss */
|
||||
public Date getDateTime(String key) {
|
||||
return getDateByFormat(key, "yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
|
||||
/** 获取集合(必须原先就是个集合,否则会创建个新集合并返回) */
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Object> getList(String key) {
|
||||
Object value = get(key);
|
||||
List<Object> list = null;
|
||||
if(value == null || value.equals("")) {
|
||||
list = new ArrayList<Object>();
|
||||
}
|
||||
else if(value instanceof List) {
|
||||
list = (List<Object>)value;
|
||||
} else {
|
||||
list = new ArrayList<Object>();
|
||||
list.add(value);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/** 获取集合 (指定泛型类型) */
|
||||
public <T> List<T> getList(String key, Class<T> cs) {
|
||||
List<Object> list = getList(key);
|
||||
List<T> list2 = new ArrayList<T>();
|
||||
for (Object obj : list) {
|
||||
T objC = getValueByClass(obj, cs);
|
||||
list2.add(objC);
|
||||
}
|
||||
return list2;
|
||||
}
|
||||
|
||||
/** 获取集合(逗号分隔式),(指定类型) */
|
||||
public <T> List<T> getListByComma(String key, Class<T> cs) {
|
||||
String listStr = getString(key);
|
||||
if(listStr == null || listStr.equals("")) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
// 开始转化
|
||||
String [] arr = listStr.split(",");
|
||||
List<T> list = new ArrayList<T>();
|
||||
for (String str : arr) {
|
||||
if(cs == int.class || cs == Integer.class || cs == long.class || cs == Long.class) {
|
||||
str = str.trim();
|
||||
}
|
||||
T objC = getValueByClass(str, cs);
|
||||
list.add(objC);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/** 根据指定类型从map中取值,返回实体对象 */
|
||||
public <T> T getModel(Class<T> cs) {
|
||||
try {
|
||||
return getModelByObject(cs.newInstance());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** 从map中取值,塞到一个对象中 */
|
||||
public <T> T getModelByObject(T obj) {
|
||||
// 获取类型
|
||||
Class<?> cs = obj.getClass();
|
||||
// 循环复制
|
||||
for (Field field : cs.getDeclaredFields()) {
|
||||
try {
|
||||
// 获取对象
|
||||
Object value = this.get(field.getName());
|
||||
if(value == null) {
|
||||
continue;
|
||||
}
|
||||
field.setAccessible(true);
|
||||
Object valueConvert = getValueByClass(value, field.getType());
|
||||
field.set(obj, valueConvert);
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
throw new RuntimeException("属性取值出错:" + field.getName(), e);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 将指定值转化为指定类型并返回
|
||||
* @param obj
|
||||
* @param cs
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getValueByClass(Object obj, Class<T> cs) {
|
||||
String obj2 = String.valueOf(obj);
|
||||
Object obj3 = null;
|
||||
if (cs.equals(String.class)) {
|
||||
obj3 = obj2;
|
||||
} else if (cs.equals(int.class) || cs.equals(Integer.class)) {
|
||||
obj3 = Integer.valueOf(obj2);
|
||||
} else if (cs.equals(long.class) || cs.equals(Long.class)) {
|
||||
obj3 = Long.valueOf(obj2);
|
||||
} else if (cs.equals(short.class) || cs.equals(Short.class)) {
|
||||
obj3 = Short.valueOf(obj2);
|
||||
} else if (cs.equals(byte.class) || cs.equals(Byte.class)) {
|
||||
obj3 = Byte.valueOf(obj2);
|
||||
} else if (cs.equals(float.class) || cs.equals(Float.class)) {
|
||||
obj3 = Float.valueOf(obj2);
|
||||
} else if (cs.equals(double.class) || cs.equals(Double.class)) {
|
||||
obj3 = Double.valueOf(obj2);
|
||||
} else if (cs.equals(boolean.class) || cs.equals(Boolean.class)) {
|
||||
obj3 = Boolean.valueOf(obj2);
|
||||
} else {
|
||||
obj3 = (T)obj;
|
||||
}
|
||||
return (T)obj3;
|
||||
}
|
||||
|
||||
|
||||
// ============================= 写值 =============================
|
||||
|
||||
/**
|
||||
* 给指定key添加一个默认值(只有在这个key原来无值的情况先才会set进去)
|
||||
*/
|
||||
public void setDefaultValue(String key, Object defaultValue) {
|
||||
if(isNull(key)) {
|
||||
set(key, defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
/** set一个值,连缀风格 */
|
||||
public SoMap set(String key, Object value) {
|
||||
// 防止敏感key
|
||||
if(key.toLowerCase().equals("this")) {
|
||||
return this;
|
||||
}
|
||||
put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 将一个Map塞进SoMap */
|
||||
public SoMap setMap(Map<String, ?> map) {
|
||||
if(map != null) {
|
||||
for (String key : map.keySet()) {
|
||||
this.set(key, map.get(key));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 将一个对象解析塞进SoMap */
|
||||
public SoMap setModel(Object model) {
|
||||
if(model == null) {
|
||||
return this;
|
||||
}
|
||||
Field[] fields = model.getClass().getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
try{
|
||||
field.setAccessible(true);
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if(!isStatic) {
|
||||
this.set(field.getName(), field.get(model));
|
||||
}
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 将json字符串解析后塞进SoMap */
|
||||
public SoMap setJsonString(String jsonString) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = new ObjectMapper().readValue(jsonString, Map.class);
|
||||
return this.setMap(map);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================= 删值 =============================
|
||||
|
||||
/** delete一个值,连缀风格 */
|
||||
public SoMap delete(String key) {
|
||||
remove(key);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 清理所有value为null的字段 */
|
||||
public SoMap clearNull() {
|
||||
Iterator<String> iterator = this.keySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
String key = iterator.next();
|
||||
if(this.isNull(key)) {
|
||||
iterator.remove();
|
||||
this.remove(key);
|
||||
}
|
||||
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 清理指定key */
|
||||
public SoMap clearIn(String ...keys) {
|
||||
List<String> keys2 = Arrays.asList(keys);
|
||||
Iterator<String> iterator = this.keySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
String key = iterator.next();
|
||||
if(keys2.contains(key) == true) {
|
||||
iterator.remove();
|
||||
this.remove(key);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 清理掉不在列表中的key */
|
||||
public SoMap clearNotIn(String ...keys) {
|
||||
List<String> keys2 = Arrays.asList(keys);
|
||||
Iterator<String> iterator = this.keySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
String key = iterator.next();
|
||||
if(keys2.contains(key) == false) {
|
||||
iterator.remove();
|
||||
this.remove(key);
|
||||
}
|
||||
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 清理掉所有key */
|
||||
public SoMap clearAll() {
|
||||
clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ============================= 快速构建 =============================
|
||||
|
||||
/** 构建一个SoMap并返回 */
|
||||
public static SoMap getSoMap() {
|
||||
return new SoMap();
|
||||
}
|
||||
/** 构建一个SoMap并返回 */
|
||||
public static SoMap getSoMap(String key, Object value) {
|
||||
return new SoMap().set(key, value);
|
||||
}
|
||||
/** 构建一个SoMap并返回 */
|
||||
public static SoMap getSoMap(Map<String, ?> map) {
|
||||
return new SoMap().setMap(map);
|
||||
}
|
||||
|
||||
/** 将一个对象集合解析成为SoMap */
|
||||
public static SoMap getSoMapByModel(Object model) {
|
||||
return SoMap.getSoMap().setModel(model);
|
||||
}
|
||||
|
||||
/** 将一个对象集合解析成为SoMap集合 */
|
||||
public static List<SoMap> getSoMapByList(List<?> list) {
|
||||
List<SoMap> listMap = new ArrayList<SoMap>();
|
||||
for (Object model : list) {
|
||||
listMap.add(getSoMapByModel(model));
|
||||
}
|
||||
return listMap;
|
||||
}
|
||||
|
||||
/** 克隆指定key,返回一个新的SoMap */
|
||||
public SoMap cloneKeys(String... keys) {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : keys) {
|
||||
so.set(key, this.get(key));
|
||||
}
|
||||
return so;
|
||||
}
|
||||
/** 克隆所有key,返回一个新的SoMap */
|
||||
public SoMap cloneSoMap() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(key, this.get(key));
|
||||
}
|
||||
return so;
|
||||
}
|
||||
|
||||
/** 将所有key转为大写 */
|
||||
public SoMap toUpperCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(key.toUpperCase(), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key转为小写 */
|
||||
public SoMap toLowerCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(key.toLowerCase(), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key中下划线转为中划线模式 (kebab-case风格) */
|
||||
public SoMap toKebabCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(wordEachKebabCase(key), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key中下划线转为小驼峰模式 */
|
||||
public SoMap toHumpCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(wordEachBigFs(key), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key中小驼峰转为下划线模式 */
|
||||
public SoMap humpToLineCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(wordHumpToLine(key), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ============================= 辅助方法 =============================
|
||||
|
||||
|
||||
/** 指定key是否为null,判定标准为 NULL_ELEMENT_ARRAY 中的元素 */
|
||||
public boolean isNull(String key) {
|
||||
return valueIsNull(get(key));
|
||||
}
|
||||
|
||||
/** 指定key列表中是否包含value为null的元素,只要有一个为null,就会返回true */
|
||||
public boolean isContainNull(String ...keys) {
|
||||
for (String key : keys) {
|
||||
if(this.isNull(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 与isNull()相反 */
|
||||
public boolean isNotNull(String key) {
|
||||
return !isNull(key);
|
||||
}
|
||||
/** 指定key的value是否为null,作用同isNotNull() */
|
||||
public boolean has(String key) {
|
||||
return !isNull(key);
|
||||
}
|
||||
|
||||
/** 指定value在此SoMap的判断标准中是否为null */
|
||||
public boolean valueIsNull(Object value) {
|
||||
return NULL_ELEMENT_LIST.contains(value);
|
||||
}
|
||||
|
||||
/** 验证指定key不为空,为空则抛出异常 */
|
||||
public SoMap checkNull(String ...keys) {
|
||||
for (String key : keys) {
|
||||
if(this.isNull(key)) {
|
||||
throw new RuntimeException("参数" + key + "不能为空");
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
static Pattern patternNumber = Pattern.compile("[0-9]*");
|
||||
/** 指定key是否为数字 */
|
||||
public boolean isNumber(String key) {
|
||||
String value = getString(key);
|
||||
if(value == null) {
|
||||
return false;
|
||||
}
|
||||
return patternNumber.matcher(value).matches();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 转为JSON字符串
|
||||
*/
|
||||
public String toJsonString() {
|
||||
try {
|
||||
// SoMap so = SoMap.getSoMap(this);
|
||||
return new ObjectMapper().writeValueAsString(this);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 转为JSON字符串, 带格式的
|
||||
// */
|
||||
// public String toJsonFormatString() {
|
||||
// try {
|
||||
// return JSON.toJSONString(this, true);
|
||||
// } catch (Exception e) {
|
||||
// throw new RuntimeException(e);
|
||||
// }
|
||||
// }
|
||||
|
||||
// ============================= web辅助 =============================
|
||||
|
||||
|
||||
/**
|
||||
* 返回当前request请求的的所有参数
|
||||
* @return
|
||||
*/
|
||||
public static SoMap getRequestSoMap() {
|
||||
// 大善人SpringMVC提供的封装
|
||||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if(servletRequestAttributes == null) {
|
||||
throw new RuntimeException("当前线程非JavaWeb环境");
|
||||
}
|
||||
// 当前request
|
||||
HttpServletRequest request = servletRequestAttributes.getRequest();
|
||||
if (request.getAttribute("currentSoMap") == null || request.getAttribute("currentSoMap") instanceof SoMap == false ) {
|
||||
initRequestSoMap(request);
|
||||
}
|
||||
return (SoMap)request.getAttribute("currentSoMap");
|
||||
}
|
||||
|
||||
/** 初始化当前request的 SoMap */
|
||||
private static void initRequestSoMap(HttpServletRequest request) {
|
||||
SoMap soMap = new SoMap();
|
||||
Map<String, String[]> parameterMap = request.getParameterMap(); // 获取所有参数
|
||||
for (String key : parameterMap.keySet()) {
|
||||
try {
|
||||
String[] values = parameterMap.get(key); // 获得values
|
||||
if(values.length == 1) {
|
||||
soMap.set(key, values[0]);
|
||||
} else {
|
||||
List<String> list = new ArrayList<String>();
|
||||
for (String v : values) {
|
||||
list.add(v);
|
||||
}
|
||||
soMap.set(key, list);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
request.setAttribute("currentSoMap", soMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证返回当前线程是否为JavaWeb环境
|
||||
* @return
|
||||
*/
|
||||
public static boolean isJavaWeb() {
|
||||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();// 大善人SpringMVC提供的封装
|
||||
if(servletRequestAttributes == null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============================= 常见key (以下key经常用,所以封装以下,方便写代码) =============================
|
||||
|
||||
/** get 当前页 */
|
||||
public int getKeyPageNo() {
|
||||
int pageNo = getInt("pageNo", 1);
|
||||
if(pageNo <= 0) {
|
||||
pageNo = 1;
|
||||
}
|
||||
return pageNo;
|
||||
}
|
||||
/** get 页大小 */
|
||||
public int getKeyPageSize() {
|
||||
int pageSize = getInt("pageSize", 10);
|
||||
if(pageSize <= 0 || pageSize > 1000) {
|
||||
pageSize = 10;
|
||||
}
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
/** get 排序方式 */
|
||||
public int getKeySortType() {
|
||||
return getInt("sortType");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ============================= 工具方法 =============================
|
||||
|
||||
|
||||
/**
|
||||
* 将一个一维集合转换为树形集合
|
||||
* @param list 集合
|
||||
* @param idKey id标识key
|
||||
* @param parentIdKey 父id标识key
|
||||
* @param childListKey 子节点标识key
|
||||
* @return 转换后的tree集合
|
||||
*/
|
||||
public static List<SoMap> listToTree(List<SoMap> list, String idKey, String parentIdKey, String childListKey) {
|
||||
// 声明新的集合,存储tree形数据
|
||||
List<SoMap> newTreeList = new ArrayList<SoMap>();
|
||||
// 声明hash-Map,方便查找数据
|
||||
SoMap hash = new SoMap();
|
||||
// 将数组转为Object的形式,key为数组中的id
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
SoMap json = (SoMap) list.get(i);
|
||||
hash.put(json.getString(idKey), json);
|
||||
}
|
||||
// 遍历结果集
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
// 单条记录
|
||||
SoMap aVal = (SoMap) list.get(j);
|
||||
// 在hash中取出key为单条记录中pid的值
|
||||
SoMap hashVp = (SoMap) hash.get(aVal.get(parentIdKey, "").toString());
|
||||
// 如果记录的pid存在,则说明它有父节点,将她添加到孩子节点的集合中
|
||||
if (hashVp != null) {
|
||||
// 检查是否有child属性,有则添加,没有则新建
|
||||
if (hashVp.get(childListKey) != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<SoMap> ch = (List<SoMap>) hashVp.get(childListKey);
|
||||
ch.add(aVal);
|
||||
hashVp.put(childListKey, ch);
|
||||
} else {
|
||||
List<SoMap> ch = new ArrayList<SoMap>();
|
||||
ch.add(aVal);
|
||||
hashVp.put(childListKey, ch);
|
||||
}
|
||||
} else {
|
||||
newTreeList.add(aVal);
|
||||
}
|
||||
}
|
||||
return newTreeList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 指定字符串的字符串下划线转大写模式 */
|
||||
private static String wordEachBig(String str){
|
||||
String newStr = "";
|
||||
for (String s : str.split("_")) {
|
||||
newStr += wordFirstBig(s);
|
||||
}
|
||||
return newStr;
|
||||
}
|
||||
/** 返回下划线转小驼峰形式 */
|
||||
private static String wordEachBigFs(String str){
|
||||
return wordFirstSmall(wordEachBig(str));
|
||||
}
|
||||
|
||||
/** 将指定单词首字母大写 */
|
||||
private static String wordFirstBig(String str) {
|
||||
return str.substring(0, 1).toUpperCase() + str.substring(1, str.length());
|
||||
}
|
||||
|
||||
/** 将指定单词首字母小写 */
|
||||
private static String wordFirstSmall(String str) {
|
||||
return str.substring(0, 1).toLowerCase() + str.substring(1, str.length());
|
||||
}
|
||||
|
||||
/** 下划线转中划线 */
|
||||
private static String wordEachKebabCase(String str) {
|
||||
return str.replaceAll("_", "-");
|
||||
}
|
||||
|
||||
/** 驼峰转下划线 */
|
||||
private static String wordHumpToLine(String str) {
|
||||
return str.replaceAll("[A-Z]", "_$0").toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
server:
|
||||
port: 8002
|
||||
|
||||
spring:
|
||||
# 静态文件路径映射
|
||||
resources:
|
||||
static-locations: classpath:/META-INF/resources/,classpath:/resources/, classpath:/static/, classpath:/public/
|
||||
# static-locations: file:E:\work\project-yun\sa-token\sa-token-demo-oauth2\sa-token-demo-oauth2-client\src\main\resources\static\
|
||||
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# token名称 (同时也是cookie名称)
|
||||
token-name: satoken-client
|
||||
|
@ -0,0 +1,127 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>客户端-登录页</title>
|
||||
<style type="text/css">
|
||||
*{margin: 0px; padding: 0px;}
|
||||
.login-box{width: 500px; margin: 50px auto;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-box">
|
||||
<h2>客户端-登录页</h2> <br>
|
||||
<div>
|
||||
当前是否登录: <span class="login-info"></span>
|
||||
<button onclick="logout()">注销登录</button>
|
||||
</div>
|
||||
<br/>
|
||||
点此按钮开始使用OAuth2.0开放平台快捷登录:
|
||||
<button onclick="requestOAuth2()">快捷登录</button>
|
||||
</div>
|
||||
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
// 获取登录信息
|
||||
var currUserId = null;
|
||||
function getLoginInfo() {
|
||||
$.ajax({
|
||||
url: '/getLoginInfo',
|
||||
data: {},
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
if(res.data == null) {
|
||||
$(".login-info").html('<b style="color: red;">未登录</b>');
|
||||
// 如果当前未登录,并且此时url中有code参数, 则尝试使用code码进行登录
|
||||
var code = getParam('code', null);
|
||||
if(code != null) {
|
||||
doCodeLogin();
|
||||
}
|
||||
} else {
|
||||
$(".login-info").html('<b style="color: green;">已登录, userId=' + res.data + '</b>');
|
||||
currUserId = res.data;
|
||||
}
|
||||
},
|
||||
error: function(e, ee, eee) {
|
||||
console.log('error');
|
||||
alert(eee);
|
||||
}
|
||||
});
|
||||
}
|
||||
getLoginInfo();
|
||||
|
||||
// 注销登录
|
||||
function logout() {
|
||||
$.ajax({
|
||||
url: '/logout',
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
alert('注销成功');
|
||||
location.href = "./login.html";
|
||||
},
|
||||
error: function(e, ee, eee) {
|
||||
alert(eee);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 请求OAuth2授权
|
||||
function requestOAuth2() {
|
||||
// 如果当前已经登录,则必须先退出
|
||||
if(currUserId != null) {
|
||||
alert('当前已经登录, 请先注销');
|
||||
return;
|
||||
}
|
||||
// 拼接地址
|
||||
var url = "http://localhost:8001/oauth2/authorize" +
|
||||
"?client_id=123123123" + // 应用id
|
||||
"&scope=userinfo" + // 授权范围
|
||||
// "&redirect_uri=" + encodeURIComponent(location.href) // 重定向地址
|
||||
"&redirect_uri=" + encodeURIComponent("http://localhost:8002/login.html") // 重定向地址
|
||||
"&response_type=code" + // 返回格式
|
||||
"&state=123456789"; // 授权范围
|
||||
console.log(url);
|
||||
location.href = url;
|
||||
}
|
||||
|
||||
// 使用code进行 (从url中获取code码进行登录)
|
||||
function doCodeLogin() {
|
||||
var code = getParam('code');
|
||||
$.ajax({
|
||||
url: '/doCodeLogin',
|
||||
data: {
|
||||
code: code
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
if(res.code == 200) {
|
||||
alert('OAuth2登录成功');
|
||||
getLoginInfo(); // 刷新user信息
|
||||
} else {
|
||||
alert("登录失败:" + res.msg);
|
||||
}
|
||||
},
|
||||
error: function(e, ee, eee) {
|
||||
alert(eee);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 从url中查询到指定名称的参数值
|
||||
function getParam(name, defaultValue){
|
||||
var query = window.location.search.substring(1);
|
||||
var vars = query.split("&");
|
||||
for (var i=0;i<vars.length;i++) {
|
||||
var pair = vars[i].split("=");
|
||||
if(pair[0] == name){return pair[1];}
|
||||
}
|
||||
return(defaultValue == undefined ? null : defaultValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
13
sa-token-demo-oauth2/sa-token-demo-oauth2-server/.gitignore
vendored
Normal file
13
sa-token-demo-oauth2/sa-token-demo-oauth2-server/.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
target/
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
|
||||
/.idea/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
/.apt_generated/
|
||||
/.apt_generated_tests/
|
66
sa-token-demo-oauth2/sa-token-demo-oauth2-server/pom.xml
Normal file
66
sa-token-demo-oauth2/sa-token-demo-oauth2-server/pom.xml
Normal file
@ -0,0 +1,66 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.pj</groupId>
|
||||
<artifactId>sa-token-demo-oauth2-server</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.3.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<!-- 指定一些属性 -->
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- springboot依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.13.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token 实现 oauth2.0 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-oauth2</artifactId>
|
||||
<version>1.13.0-alpha</version>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token整合redis (使用jackson序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>1.13.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency> -->
|
||||
|
||||
<!-- ConfigurationProperties -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
|
||||
</project>
|
@ -0,0 +1,18 @@
|
||||
package com.pj;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 启动
|
||||
* @author kong
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class SaOAuth2ServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaOAuth2ServerApplication.class, args);
|
||||
System.out.println("\n服务端启动成功");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package com.pj.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import com.pj.utils.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
|
||||
/**
|
||||
* 全局异常拦截
|
||||
* <p> @ControllerAdvice 可指定包前缀,例如:(basePackages = "com.pj.controller.admin")
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@ControllerAdvice
|
||||
public class ExceptionHandle {
|
||||
|
||||
|
||||
/** 全局异常拦截 */
|
||||
@ResponseBody
|
||||
@ExceptionHandler
|
||||
public AjaxJson handlerException(Exception e) {
|
||||
|
||||
// 打印堆栈,以供调试
|
||||
e.printStackTrace();
|
||||
|
||||
// 记录日志信息
|
||||
AjaxJson aj = null;
|
||||
|
||||
// ------------- 判断异常类型,提供个性化提示信息
|
||||
|
||||
// 如果是未登录异常
|
||||
if(e instanceof NotLoginException){
|
||||
aj = AjaxJson.getNotLogin();
|
||||
}
|
||||
// 如果是角色异常
|
||||
else if(e instanceof NotRoleException) {
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此角色:" + ee.getCode());
|
||||
}
|
||||
// 如果是权限异常
|
||||
else if(e instanceof NotPermissionException) {
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getCode());
|
||||
}
|
||||
// 普通异常输出:500 + 异常信息
|
||||
else {
|
||||
aj = AjaxJson.getError(e.getMessage());
|
||||
}
|
||||
|
||||
// 返回到前台
|
||||
return aj;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
package com.pj.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLDecoder;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.pj.utils.AjaxJson;
|
||||
import com.pj.utils.SoMap;
|
||||
|
||||
import cn.dev33.satoken.oauth2.logic.SaOAuth2Util;
|
||||
import cn.dev33.satoken.oauth2.model.AccessTokenModel;
|
||||
import cn.dev33.satoken.oauth2.model.CodeModel;
|
||||
import cn.dev33.satoken.oauth2.model.RequestAuthModel;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/oauth2/")
|
||||
public class OAuth2Controller {
|
||||
|
||||
|
||||
// 获取授权码
|
||||
@RequestMapping("/authorize")
|
||||
public AjaxJson authorize(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||
// 获取参数
|
||||
System.out.println("------------------ 成功进入请求 ------------------");
|
||||
|
||||
// 如果暂未登录,则先跳转到登录页 (转发)
|
||||
if(StpUtil.isLogin() == false) {
|
||||
response.setContentType("text/html");
|
||||
request.getRequestDispatcher("/login.html").forward(request, response);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 构建Model
|
||||
RequestAuthModel authModel = new RequestAuthModel()
|
||||
.setClientId(request.getParameter("client_id")) // 应用id
|
||||
.setScope(request.getParameter("scope")) // 授权类型
|
||||
.setLoginId(StpUtil.getLoginIdAsLong()) // 当前登录账号id
|
||||
.setRedirectUri(URLDecoder.decode(request.getParameter("redirect_uri"), "utf-8")) // 重定向地址
|
||||
.setResponseType(request.getParameter("response_type")) // 返回类型
|
||||
.setState(request.getParameter("state")) // 状态值
|
||||
.checkModel(); // 校验参数完整性
|
||||
|
||||
// 生成授权码Model
|
||||
CodeModel codeModel = SaOAuth2Util.generateCode(authModel);
|
||||
|
||||
// 打印调试
|
||||
System.out.println("应用id=" + authModel.getClientId() + "请求授权,授权类型=" + authModel.getResponseType());
|
||||
System.out.println("重定向地址:" + authModel.getRedirectUri());
|
||||
System.out.println("拼接完成的redirect_uri: " + codeModel.getRedirectUri());
|
||||
System.out.println("如果用户拒绝授权,则重定向至: " + codeModel.getRejectUri());
|
||||
|
||||
// 如果请求的权限用户已经确认,直接开始重定向授权
|
||||
if(codeModel.getIsConfirm() == true) {
|
||||
response.sendRedirect(codeModel.getRedirectUri());
|
||||
} else {
|
||||
// 如果请求的权限用户尚未确认,则进入到确定页
|
||||
request.setAttribute("name", "sdd");
|
||||
response.sendRedirect("/auth.html?code=" + codeModel.getCode());
|
||||
}
|
||||
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 根据授权码获取应用信息
|
||||
@RequestMapping("/getCodeInfo")
|
||||
public AjaxJson getCodeInfo(String code) {
|
||||
// 获取codeModel
|
||||
CodeModel codeModel = SaOAuth2Util.getCode(code);
|
||||
System.out.println(code);
|
||||
System.out.println(codeModel);
|
||||
// 返回
|
||||
return AjaxJson.getSuccessData(codeModel);
|
||||
}
|
||||
|
||||
// 确认授权一个授权码
|
||||
@RequestMapping("/confirm")
|
||||
public AjaxJson confirm(String code) {
|
||||
// 获取codeModel
|
||||
CodeModel codeModel = SaOAuth2Util.getCode(code);
|
||||
if(codeModel == null) {
|
||||
return AjaxJson.getError("无效code码");
|
||||
}
|
||||
// 此处的判断是为了保证当前账号id 和 创建授权码的账号id一致 才可以进行确认
|
||||
if(codeModel.getLoginId().toString().equals(StpUtil.getLoginIdAsString()) == false) {
|
||||
return AjaxJson.getError("暂无权限");
|
||||
}
|
||||
// 进行确认
|
||||
SaOAuth2Util.confirmCode(code);
|
||||
|
||||
// 返回ok
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 根据授权码等参数,获取 access_token 等信息
|
||||
@RequestMapping("/getAccessToken")
|
||||
public SoMap getAccessToken(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
// 获取参数
|
||||
System.out.println("------------------ 成功进入请求 ------------------");
|
||||
String code = request.getParameter("code"); // code码
|
||||
String clientId = request.getParameter("client_id"); // 应用id
|
||||
String clientSecret = request.getParameter("client_secret"); // 应用秘钥
|
||||
|
||||
// 校验参数
|
||||
SaOAuth2Util.checkCodeIdSecret(code, clientId, clientSecret);
|
||||
|
||||
// 生成
|
||||
CodeModel codeModel = SaOAuth2Util.getCode(code);
|
||||
AccessTokenModel tokenModel = SaOAuth2Util.generateAccessToken(codeModel);
|
||||
|
||||
// 生成AccessToken之后,将授权码立即销毁
|
||||
SaOAuth2Util.deleteCode(code);
|
||||
|
||||
// 返回
|
||||
return SoMap.getSoMap()
|
||||
.setModel(tokenModel)
|
||||
.set("code", 200)
|
||||
.set("msg", "ok");
|
||||
}
|
||||
|
||||
// 根据access_token返回指定的资源
|
||||
@RequestMapping("/getResources")
|
||||
public SoMap getResources(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
|
||||
// 获取信息
|
||||
String accessToken = request.getParameter("access_token");
|
||||
Object LoginId = SaOAuth2Util.getLoginIdByAccessToken(accessToken);
|
||||
System.out.println("LoginId=" + LoginId);
|
||||
|
||||
// 根据LoginId获取相应信息...
|
||||
// 此处仅做模拟
|
||||
return new SoMap()
|
||||
.set("nickname", "shengzhang")
|
||||
.set("acatar", "xxx")
|
||||
.set("sex", 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.pj.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.pj.utils.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 服务端登录Controller
|
||||
* @author kong
|
||||
*/
|
||||
@RestController
|
||||
public class ServerAccController {
|
||||
|
||||
// 登录方法
|
||||
@RequestMapping("/doLogin")
|
||||
public AjaxJson test(String username, String password) {
|
||||
System.out.println("------------------ 成功进入请求 ------------------");
|
||||
if("test".equals(username) && "test".equals(password)) {
|
||||
StpUtil.setLoginId(10001);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
return AjaxJson.getError();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package com.pj.oauth2;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.oauth2.logic.SaOAuth2Interface;
|
||||
|
||||
/**
|
||||
* 使用oauth2.0 所必须的一些自定义实现
|
||||
* @author kong
|
||||
*/
|
||||
@Component
|
||||
public class SaOAuth2InterfaceImpl implements SaOAuth2Interface {
|
||||
|
||||
|
||||
/*
|
||||
* ------ 注意: 以下代码均为示例,真实环境需要根据数据库查询相关信息
|
||||
*/
|
||||
|
||||
// 返回此平台所有权限集合
|
||||
@Override
|
||||
public List<String> getAppScopeList() {
|
||||
return Arrays.asList("userinfo");
|
||||
}
|
||||
|
||||
// 返回指定Client签约的所有Scope集合
|
||||
@Override
|
||||
public List<String> getClientScopeList(String clientId) {
|
||||
return Arrays.asList("userinfo");
|
||||
}
|
||||
|
||||
// 获取指定 LoginId 对指定 Client 已经授权过的所有 Scope
|
||||
@Override
|
||||
public List<String> getGrantScopeList(Object loginId, String clientId) {
|
||||
return Arrays.asList();
|
||||
}
|
||||
|
||||
// 返回指定Client允许的回调域名, 多个用逗号隔开, *代表不限制
|
||||
@Override
|
||||
public String getClientDomain(String clientId) {
|
||||
return "*";
|
||||
}
|
||||
|
||||
// 返回指定ClientId的ClientSecret
|
||||
@Override
|
||||
public String getClientSecret(String clientId) {
|
||||
return "aaaa-bbbb-cccc-dddd-eeee";
|
||||
}
|
||||
|
||||
// 根据ClientId和LoginId返回openid
|
||||
@Override
|
||||
public String getOpenid(String clientId, Object loginId) {
|
||||
return "gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__";
|
||||
}
|
||||
|
||||
// 根据ClientId和openid返回LoginId
|
||||
@Override
|
||||
public Object getLoginId(String clientId, String openid) {
|
||||
return 10001;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* 以上函数为开发时必须重写实现,其余函数可以按需重写
|
||||
*/
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package com.pj.oauth2;
|
||||
|
||||
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.oauth2.SaOAuth2Manager;
|
||||
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
|
||||
import cn.dev33.satoken.oauth2.logic.SaOAuth2Interface;
|
||||
|
||||
/**
|
||||
* 利用Spring完成自动装配
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class SaOAuth2SpringAutowired {
|
||||
|
||||
/**
|
||||
* 获取OAuth2配置Bean
|
||||
*
|
||||
* @return 配置对象
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.sa-token.oauth2")
|
||||
public SaOAuth2Config getSaOAuth2Config() {
|
||||
return new SaOAuth2Config();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入OAuth2配置Bean
|
||||
*
|
||||
* @param saOAuth2Config 配置对象
|
||||
*/
|
||||
@Autowired
|
||||
public void setSaOAuth2Config(SaOAuth2Config saOAuth2Config) {
|
||||
SaOAuth2Manager.setConfig(saOAuth2Config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入OAuth2接口Bean
|
||||
*
|
||||
* @param saOAuth2Interface OAuth2接口Bean
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setSaOAuth2Interface(SaOAuth2Interface saOAuth2Interface) {
|
||||
SaOAuth2Manager.setInterface(saOAuth2Interface);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,223 @@
|
||||
package com.pj.utils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* ajax请求返回Json格式数据的封装 <br>
|
||||
* 所有预留字段:<br>
|
||||
* code=状态码 <br>
|
||||
* msg=描述信息 <br>
|
||||
* data=携带对象 <br>
|
||||
* pageNo=当前页 <br>
|
||||
* pageSize=页大小 <br>
|
||||
* startIndex=起始索引 <br>
|
||||
* dataCount=数据总数 <br>
|
||||
* pageCount=分页总数 <br>
|
||||
* <p> 返回范例:</p>
|
||||
* <pre>
|
||||
{
|
||||
"code": 200, // 成功时=200, 失败时=500 msg=失败原因
|
||||
"msg": "ok",
|
||||
"data": {}
|
||||
}
|
||||
</pre>
|
||||
*/
|
||||
public class AjaxJson extends LinkedHashMap<String, Object> implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 1L; // 序列化版本号
|
||||
|
||||
public static final int CODE_SUCCESS = 200; // 成功状态码
|
||||
public static final int CODE_ERROR = 500; // 错误状态码
|
||||
public static final int CODE_WARNING = 501; // 警告状态码
|
||||
public static final int CODE_NOT_JUR = 403; // 无权限状态码
|
||||
public static final int CODE_NOT_LOGIN = 401; // 未登录状态码
|
||||
public static final int CODE_INVALID_REQUEST = 400; // 无效请求状态码
|
||||
|
||||
|
||||
|
||||
// ============================ 写值取值 ==================================
|
||||
|
||||
/** 给code赋值,连缀风格 */
|
||||
public AjaxJson setCode(int code) {
|
||||
this.put("code", code);
|
||||
return this;
|
||||
}
|
||||
/** 返回code */
|
||||
public Integer getCode() {
|
||||
return (Integer)this.get("code");
|
||||
}
|
||||
|
||||
/** 给msg赋值,连缀风格 */
|
||||
public AjaxJson setMsg(String msg) {
|
||||
this.put("msg", msg);
|
||||
return this;
|
||||
}
|
||||
/** 获取msg */
|
||||
public String getMsg() {
|
||||
return (String)this.get("msg");
|
||||
}
|
||||
|
||||
/** 给data赋值,连缀风格 */
|
||||
public AjaxJson setData(Object data) {
|
||||
this.put("data", data);
|
||||
return this;
|
||||
}
|
||||
/** 获取data */
|
||||
public String getData() {
|
||||
return (String)this.get("data");
|
||||
}
|
||||
/** 将data还原为指定类型并返回 */
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getData(Class<T> cs) {
|
||||
return (T) this.getData();
|
||||
}
|
||||
|
||||
/** 给dataCount(数据总数)赋值,连缀风格 */
|
||||
public AjaxJson setDataCount(Long dataCount) {
|
||||
this.put("dataCount", dataCount);
|
||||
// 如果提供了数据总数,则尝试计算page信息
|
||||
if(dataCount != null && dataCount >= 0) {
|
||||
// 如果:已有page信息
|
||||
if(get("pageNo") != null) {
|
||||
this.initPageInfo();
|
||||
}
|
||||
// // 或者:是JavaWeb环境
|
||||
// else if(SoMap.isJavaWeb()) {
|
||||
// SoMap so = SoMap.getRequestSoMap();
|
||||
// this.setPageNoAndSize(so.getKeyPageNo(), so.getKeyPageSize());
|
||||
// this.initPageInfo();
|
||||
// }
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 获取dataCount(数据总数) */
|
||||
public String getDataCount() {
|
||||
return (String)this.get("dataCount");
|
||||
}
|
||||
|
||||
/** 设置pageNo 和 pageSize,并计算出startIndex于pageCount */
|
||||
public AjaxJson setPageNoAndSize(long pageNo, long pageSize) {
|
||||
this.put("pageNo", pageNo);
|
||||
this.put("pageSize", pageSize);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 根据 pageSize dataCount,计算startIndex 与 pageCount */
|
||||
public AjaxJson initPageInfo() {
|
||||
long pageNo = (long)this.get("pageNo");
|
||||
long pageSize = (long)this.get("pageSize");
|
||||
long dataCount = (long)this.get("dataCount");
|
||||
this.set("startIndex", (pageNo - 1) * pageSize);
|
||||
long pc = dataCount / pageSize;
|
||||
this.set("pageCount", (dataCount % pageSize == 0 ? pc : pc + 1));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/** 写入一个值 自定义key, 连缀风格 */
|
||||
public AjaxJson set(String key, Object data) {
|
||||
this.put(key, data);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 写入一个Map, 连缀风格 */
|
||||
public AjaxJson setMap(Map<String, ?> map) {
|
||||
for (String key : map.keySet()) {
|
||||
this.put(key, map.get(key));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ============================ 构建 ==================================
|
||||
|
||||
public AjaxJson(int code, String msg, Object data, Long dataCount) {
|
||||
this.setCode(code);
|
||||
this.setMsg(msg);
|
||||
this.setData(data);
|
||||
this.setDataCount(dataCount == null ? -1 : dataCount);
|
||||
}
|
||||
|
||||
/** 返回成功 */
|
||||
public static AjaxJson getSuccess() {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg, Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, data, null);
|
||||
}
|
||||
public static AjaxJson getSuccessData(Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||
}
|
||||
|
||||
|
||||
/** 返回失败 */
|
||||
public static AjaxJson getError() {
|
||||
return new AjaxJson(CODE_ERROR, "error", null, null);
|
||||
}
|
||||
public static AjaxJson getError(String msg) {
|
||||
return new AjaxJson(CODE_ERROR, msg, null, null);
|
||||
}
|
||||
|
||||
/** 返回警告 */
|
||||
public static AjaxJson getWarning() {
|
||||
return new AjaxJson(CODE_ERROR, "warning", null, null);
|
||||
}
|
||||
public static AjaxJson getWarning(String msg) {
|
||||
return new AjaxJson(CODE_WARNING, msg, null, null);
|
||||
}
|
||||
|
||||
/** 返回未登录 */
|
||||
public static AjaxJson getNotLogin() {
|
||||
return new AjaxJson(CODE_NOT_LOGIN, "未登录,请登录后再次访问", null, null);
|
||||
}
|
||||
|
||||
/** 返回没有权限的 */
|
||||
public static AjaxJson getNotJur(String msg) {
|
||||
return new AjaxJson(CODE_NOT_JUR, msg, null, null);
|
||||
}
|
||||
|
||||
/** 返回一个自定义状态码的 */
|
||||
public static AjaxJson get(int code, String msg){
|
||||
return new AjaxJson(code, msg, null, null);
|
||||
}
|
||||
|
||||
/** 返回分页和数据的 */
|
||||
public static AjaxJson getPageData(Long dataCount, Object data){
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, dataCount);
|
||||
}
|
||||
|
||||
/** 返回, 根据受影响行数的(大于0=ok,小于0=error) */
|
||||
public static AjaxJson getByLine(int line){
|
||||
if(line > 0){
|
||||
return getSuccess("ok", line);
|
||||
}
|
||||
return getError("error").setData(line);
|
||||
}
|
||||
|
||||
/** 返回,根据布尔值来确定最终结果的 (true=ok,false=error) */
|
||||
public static AjaxJson getByBoolean(boolean b){
|
||||
return b ? getSuccess("ok") : getError("error");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// // 历史版本遗留代码
|
||||
// public int code; // 状态码
|
||||
// public String msg; // 描述信息
|
||||
// public Object data; // 携带对象
|
||||
// public Long dataCount; // 数据总数,用于分页
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,723 @@
|
||||
package com.pj.utils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Map< String, Object> 是最常用的一种Map类型,但是它写着麻烦
|
||||
* <p>所以特封装此类,继承Map,进行一些扩展,可以让Map更灵活使用
|
||||
* <p>最新:2020-12-10 新增部分构造方法
|
||||
* @author kong
|
||||
*/
|
||||
public class SoMap extends LinkedHashMap<String, Object> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public SoMap() {
|
||||
}
|
||||
|
||||
|
||||
/** 以下元素会在isNull函数中被判定为Null, */
|
||||
public static final Object[] NULL_ELEMENT_ARRAY = {null, ""};
|
||||
public static final List<Object> NULL_ELEMENT_LIST;
|
||||
|
||||
|
||||
static {
|
||||
NULL_ELEMENT_LIST = Arrays.asList(NULL_ELEMENT_ARRAY);
|
||||
}
|
||||
|
||||
// ============================= 读值 =============================
|
||||
|
||||
/** 获取一个值 */
|
||||
@Override
|
||||
public Object get(Object key) {
|
||||
if("this".equals(key)) {
|
||||
return this;
|
||||
}
|
||||
return super.get(key);
|
||||
}
|
||||
|
||||
/** 如果为空,则返回默认值 */
|
||||
public Object get(Object key, Object defaultValue) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/** 转为String并返回 */
|
||||
public String getString(String key) {
|
||||
Object value = get(key);
|
||||
if(value == null) {
|
||||
return null;
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
/** 如果为空,则返回默认值 */
|
||||
public String getString(String key, String defaultValue) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
/** 转为int并返回 */
|
||||
public int getInt(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return 0;
|
||||
}
|
||||
return Integer.valueOf(String.valueOf(value));
|
||||
}
|
||||
/** 转为int并返回,同时指定默认值 */
|
||||
public int getInt(String key, int defaultValue) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Integer.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为long并返回 */
|
||||
public long getLong(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return 0;
|
||||
}
|
||||
return Long.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为double并返回 */
|
||||
public double getDouble(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return 0.0;
|
||||
}
|
||||
return Double.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为boolean并返回 */
|
||||
public boolean getBoolean(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return false;
|
||||
}
|
||||
return Boolean.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为Date并返回,根据自定义格式 */
|
||||
public Date getDateByFormat(String key, String format) {
|
||||
try {
|
||||
return new SimpleDateFormat(format).parse(getString(key));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** 转为Date并返回,根据格式: yyyy-MM-dd */
|
||||
public Date getDate(String key) {
|
||||
return getDateByFormat(key, "yyyy-MM-dd");
|
||||
}
|
||||
|
||||
/** 转为Date并返回,根据格式: yyyy-MM-dd HH:mm:ss */
|
||||
public Date getDateTime(String key) {
|
||||
return getDateByFormat(key, "yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
|
||||
/** 获取集合(必须原先就是个集合,否则会创建个新集合并返回) */
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Object> getList(String key) {
|
||||
Object value = get(key);
|
||||
List<Object> list = null;
|
||||
if(value == null || value.equals("")) {
|
||||
list = new ArrayList<Object>();
|
||||
}
|
||||
else if(value instanceof List) {
|
||||
list = (List<Object>)value;
|
||||
} else {
|
||||
list = new ArrayList<Object>();
|
||||
list.add(value);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/** 获取集合 (指定泛型类型) */
|
||||
public <T> List<T> getList(String key, Class<T> cs) {
|
||||
List<Object> list = getList(key);
|
||||
List<T> list2 = new ArrayList<T>();
|
||||
for (Object obj : list) {
|
||||
T objC = getValueByClass(obj, cs);
|
||||
list2.add(objC);
|
||||
}
|
||||
return list2;
|
||||
}
|
||||
|
||||
/** 获取集合(逗号分隔式),(指定类型) */
|
||||
public <T> List<T> getListByComma(String key, Class<T> cs) {
|
||||
String listStr = getString(key);
|
||||
if(listStr == null || listStr.equals("")) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
// 开始转化
|
||||
String [] arr = listStr.split(",");
|
||||
List<T> list = new ArrayList<T>();
|
||||
for (String str : arr) {
|
||||
if(cs == int.class || cs == Integer.class || cs == long.class || cs == Long.class) {
|
||||
str = str.trim();
|
||||
}
|
||||
T objC = getValueByClass(str, cs);
|
||||
list.add(objC);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/** 根据指定类型从map中取值,返回实体对象 */
|
||||
public <T> T getModel(Class<T> cs) {
|
||||
try {
|
||||
return getModelByObject(cs.newInstance());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** 从map中取值,塞到一个对象中 */
|
||||
public <T> T getModelByObject(T obj) {
|
||||
// 获取类型
|
||||
Class<?> cs = obj.getClass();
|
||||
// 循环复制
|
||||
for (Field field : cs.getDeclaredFields()) {
|
||||
try {
|
||||
// 获取对象
|
||||
Object value = this.get(field.getName());
|
||||
if(value == null) {
|
||||
continue;
|
||||
}
|
||||
field.setAccessible(true);
|
||||
Object valueConvert = getValueByClass(value, field.getType());
|
||||
field.set(obj, valueConvert);
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
throw new RuntimeException("属性取值出错:" + field.getName(), e);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 将指定值转化为指定类型并返回
|
||||
* @param obj
|
||||
* @param cs
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getValueByClass(Object obj, Class<T> cs) {
|
||||
String obj2 = String.valueOf(obj);
|
||||
Object obj3 = null;
|
||||
if (cs.equals(String.class)) {
|
||||
obj3 = obj2;
|
||||
} else if (cs.equals(int.class) || cs.equals(Integer.class)) {
|
||||
obj3 = Integer.valueOf(obj2);
|
||||
} else if (cs.equals(long.class) || cs.equals(Long.class)) {
|
||||
obj3 = Long.valueOf(obj2);
|
||||
} else if (cs.equals(short.class) || cs.equals(Short.class)) {
|
||||
obj3 = Short.valueOf(obj2);
|
||||
} else if (cs.equals(byte.class) || cs.equals(Byte.class)) {
|
||||
obj3 = Byte.valueOf(obj2);
|
||||
} else if (cs.equals(float.class) || cs.equals(Float.class)) {
|
||||
obj3 = Float.valueOf(obj2);
|
||||
} else if (cs.equals(double.class) || cs.equals(Double.class)) {
|
||||
obj3 = Double.valueOf(obj2);
|
||||
} else if (cs.equals(boolean.class) || cs.equals(Boolean.class)) {
|
||||
obj3 = Boolean.valueOf(obj2);
|
||||
} else {
|
||||
obj3 = (T)obj;
|
||||
}
|
||||
return (T)obj3;
|
||||
}
|
||||
|
||||
|
||||
// ============================= 写值 =============================
|
||||
|
||||
/**
|
||||
* 给指定key添加一个默认值(只有在这个key原来无值的情况先才会set进去)
|
||||
*/
|
||||
public void setDefaultValue(String key, Object defaultValue) {
|
||||
if(isNull(key)) {
|
||||
set(key, defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
/** set一个值,连缀风格 */
|
||||
public SoMap set(String key, Object value) {
|
||||
// 防止敏感key
|
||||
if(key.toLowerCase().equals("this")) {
|
||||
return this;
|
||||
}
|
||||
put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 将一个Map塞进SoMap */
|
||||
public SoMap setMap(Map<String, ?> map) {
|
||||
if(map != null) {
|
||||
for (String key : map.keySet()) {
|
||||
this.set(key, map.get(key));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 将一个对象解析塞进SoMap */
|
||||
public SoMap setModel(Object model) {
|
||||
if(model == null) {
|
||||
return this;
|
||||
}
|
||||
Field[] fields = model.getClass().getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
try{
|
||||
field.setAccessible(true);
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if(!isStatic) {
|
||||
this.set(field.getName(), field.get(model));
|
||||
}
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 将json字符串解析后塞进SoMap */
|
||||
public SoMap setJsonString(String jsonString) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = new ObjectMapper().readValue(jsonString, Map.class);
|
||||
return this.setMap(map);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================= 删值 =============================
|
||||
|
||||
/** delete一个值,连缀风格 */
|
||||
public SoMap delete(String key) {
|
||||
remove(key);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 清理所有value为null的字段 */
|
||||
public SoMap clearNull() {
|
||||
Iterator<String> iterator = this.keySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
String key = iterator.next();
|
||||
if(this.isNull(key)) {
|
||||
iterator.remove();
|
||||
this.remove(key);
|
||||
}
|
||||
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 清理指定key */
|
||||
public SoMap clearIn(String ...keys) {
|
||||
List<String> keys2 = Arrays.asList(keys);
|
||||
Iterator<String> iterator = this.keySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
String key = iterator.next();
|
||||
if(keys2.contains(key) == true) {
|
||||
iterator.remove();
|
||||
this.remove(key);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 清理掉不在列表中的key */
|
||||
public SoMap clearNotIn(String ...keys) {
|
||||
List<String> keys2 = Arrays.asList(keys);
|
||||
Iterator<String> iterator = this.keySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
String key = iterator.next();
|
||||
if(keys2.contains(key) == false) {
|
||||
iterator.remove();
|
||||
this.remove(key);
|
||||
}
|
||||
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 清理掉所有key */
|
||||
public SoMap clearAll() {
|
||||
clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ============================= 快速构建 =============================
|
||||
|
||||
/** 构建一个SoMap并返回 */
|
||||
public static SoMap getSoMap() {
|
||||
return new SoMap();
|
||||
}
|
||||
/** 构建一个SoMap并返回 */
|
||||
public static SoMap getSoMap(String key, Object value) {
|
||||
return new SoMap().set(key, value);
|
||||
}
|
||||
/** 构建一个SoMap并返回 */
|
||||
public static SoMap getSoMap(Map<String, ?> map) {
|
||||
return new SoMap().setMap(map);
|
||||
}
|
||||
|
||||
/** 将一个对象集合解析成为SoMap */
|
||||
public static SoMap getSoMapByModel(Object model) {
|
||||
return SoMap.getSoMap().setModel(model);
|
||||
}
|
||||
|
||||
/** 将一个对象集合解析成为SoMap集合 */
|
||||
public static List<SoMap> getSoMapByList(List<?> list) {
|
||||
List<SoMap> listMap = new ArrayList<SoMap>();
|
||||
for (Object model : list) {
|
||||
listMap.add(getSoMapByModel(model));
|
||||
}
|
||||
return listMap;
|
||||
}
|
||||
|
||||
/** 克隆指定key,返回一个新的SoMap */
|
||||
public SoMap cloneKeys(String... keys) {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : keys) {
|
||||
so.set(key, this.get(key));
|
||||
}
|
||||
return so;
|
||||
}
|
||||
/** 克隆所有key,返回一个新的SoMap */
|
||||
public SoMap cloneSoMap() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(key, this.get(key));
|
||||
}
|
||||
return so;
|
||||
}
|
||||
|
||||
/** 将所有key转为大写 */
|
||||
public SoMap toUpperCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(key.toUpperCase(), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key转为小写 */
|
||||
public SoMap toLowerCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(key.toLowerCase(), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key中下划线转为中划线模式 (kebab-case风格) */
|
||||
public SoMap toKebabCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(wordEachKebabCase(key), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key中下划线转为小驼峰模式 */
|
||||
public SoMap toHumpCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(wordEachBigFs(key), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key中小驼峰转为下划线模式 */
|
||||
public SoMap humpToLineCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(wordHumpToLine(key), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ============================= 辅助方法 =============================
|
||||
|
||||
|
||||
/** 指定key是否为null,判定标准为 NULL_ELEMENT_ARRAY 中的元素 */
|
||||
public boolean isNull(String key) {
|
||||
return valueIsNull(get(key));
|
||||
}
|
||||
|
||||
/** 指定key列表中是否包含value为null的元素,只要有一个为null,就会返回true */
|
||||
public boolean isContainNull(String ...keys) {
|
||||
for (String key : keys) {
|
||||
if(this.isNull(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 与isNull()相反 */
|
||||
public boolean isNotNull(String key) {
|
||||
return !isNull(key);
|
||||
}
|
||||
/** 指定key的value是否为null,作用同isNotNull() */
|
||||
public boolean has(String key) {
|
||||
return !isNull(key);
|
||||
}
|
||||
|
||||
/** 指定value在此SoMap的判断标准中是否为null */
|
||||
public boolean valueIsNull(Object value) {
|
||||
return NULL_ELEMENT_LIST.contains(value);
|
||||
}
|
||||
|
||||
/** 验证指定key不为空,为空则抛出异常 */
|
||||
public SoMap checkNull(String ...keys) {
|
||||
for (String key : keys) {
|
||||
if(this.isNull(key)) {
|
||||
throw new RuntimeException("参数" + key + "不能为空");
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
static Pattern patternNumber = Pattern.compile("[0-9]*");
|
||||
/** 指定key是否为数字 */
|
||||
public boolean isNumber(String key) {
|
||||
String value = getString(key);
|
||||
if(value == null) {
|
||||
return false;
|
||||
}
|
||||
return patternNumber.matcher(value).matches();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 转为JSON字符串
|
||||
*/
|
||||
public String toJsonString() {
|
||||
try {
|
||||
// SoMap so = SoMap.getSoMap(this);
|
||||
return new ObjectMapper().writeValueAsString(this);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 转为JSON字符串, 带格式的
|
||||
// */
|
||||
// public String toJsonFormatString() {
|
||||
// try {
|
||||
// return JSON.toJSONString(this, true);
|
||||
// } catch (Exception e) {
|
||||
// throw new RuntimeException(e);
|
||||
// }
|
||||
// }
|
||||
|
||||
// ============================= web辅助 =============================
|
||||
|
||||
|
||||
/**
|
||||
* 返回当前request请求的的所有参数
|
||||
* @return
|
||||
*/
|
||||
public static SoMap getRequestSoMap() {
|
||||
// 大善人SpringMVC提供的封装
|
||||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if(servletRequestAttributes == null) {
|
||||
throw new RuntimeException("当前线程非JavaWeb环境");
|
||||
}
|
||||
// 当前request
|
||||
HttpServletRequest request = servletRequestAttributes.getRequest();
|
||||
if (request.getAttribute("currentSoMap") == null || request.getAttribute("currentSoMap") instanceof SoMap == false ) {
|
||||
initRequestSoMap(request);
|
||||
}
|
||||
return (SoMap)request.getAttribute("currentSoMap");
|
||||
}
|
||||
|
||||
/** 初始化当前request的 SoMap */
|
||||
private static void initRequestSoMap(HttpServletRequest request) {
|
||||
SoMap soMap = new SoMap();
|
||||
Map<String, String[]> parameterMap = request.getParameterMap(); // 获取所有参数
|
||||
for (String key : parameterMap.keySet()) {
|
||||
try {
|
||||
String[] values = parameterMap.get(key); // 获得values
|
||||
if(values.length == 1) {
|
||||
soMap.set(key, values[0]);
|
||||
} else {
|
||||
List<String> list = new ArrayList<String>();
|
||||
for (String v : values) {
|
||||
list.add(v);
|
||||
}
|
||||
soMap.set(key, list);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
request.setAttribute("currentSoMap", soMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证返回当前线程是否为JavaWeb环境
|
||||
* @return
|
||||
*/
|
||||
public static boolean isJavaWeb() {
|
||||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();// 大善人SpringMVC提供的封装
|
||||
if(servletRequestAttributes == null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============================= 常见key (以下key经常用,所以封装以下,方便写代码) =============================
|
||||
|
||||
/** get 当前页 */
|
||||
public int getKeyPageNo() {
|
||||
int pageNo = getInt("pageNo", 1);
|
||||
if(pageNo <= 0) {
|
||||
pageNo = 1;
|
||||
}
|
||||
return pageNo;
|
||||
}
|
||||
/** get 页大小 */
|
||||
public int getKeyPageSize() {
|
||||
int pageSize = getInt("pageSize", 10);
|
||||
if(pageSize <= 0 || pageSize > 1000) {
|
||||
pageSize = 10;
|
||||
}
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
/** get 排序方式 */
|
||||
public int getKeySortType() {
|
||||
return getInt("sortType");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ============================= 工具方法 =============================
|
||||
|
||||
|
||||
/**
|
||||
* 将一个一维集合转换为树形集合
|
||||
* @param list 集合
|
||||
* @param idKey id标识key
|
||||
* @param parentIdKey 父id标识key
|
||||
* @param childListKey 子节点标识key
|
||||
* @return 转换后的tree集合
|
||||
*/
|
||||
public static List<SoMap> listToTree(List<SoMap> list, String idKey, String parentIdKey, String childListKey) {
|
||||
// 声明新的集合,存储tree形数据
|
||||
List<SoMap> newTreeList = new ArrayList<SoMap>();
|
||||
// 声明hash-Map,方便查找数据
|
||||
SoMap hash = new SoMap();
|
||||
// 将数组转为Object的形式,key为数组中的id
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
SoMap json = (SoMap) list.get(i);
|
||||
hash.put(json.getString(idKey), json);
|
||||
}
|
||||
// 遍历结果集
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
// 单条记录
|
||||
SoMap aVal = (SoMap) list.get(j);
|
||||
// 在hash中取出key为单条记录中pid的值
|
||||
SoMap hashVp = (SoMap) hash.get(aVal.get(parentIdKey, "").toString());
|
||||
// 如果记录的pid存在,则说明它有父节点,将她添加到孩子节点的集合中
|
||||
if (hashVp != null) {
|
||||
// 检查是否有child属性,有则添加,没有则新建
|
||||
if (hashVp.get(childListKey) != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<SoMap> ch = (List<SoMap>) hashVp.get(childListKey);
|
||||
ch.add(aVal);
|
||||
hashVp.put(childListKey, ch);
|
||||
} else {
|
||||
List<SoMap> ch = new ArrayList<SoMap>();
|
||||
ch.add(aVal);
|
||||
hashVp.put(childListKey, ch);
|
||||
}
|
||||
} else {
|
||||
newTreeList.add(aVal);
|
||||
}
|
||||
}
|
||||
return newTreeList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 指定字符串的字符串下划线转大写模式 */
|
||||
private static String wordEachBig(String str){
|
||||
String newStr = "";
|
||||
for (String s : str.split("_")) {
|
||||
newStr += wordFirstBig(s);
|
||||
}
|
||||
return newStr;
|
||||
}
|
||||
/** 返回下划线转小驼峰形式 */
|
||||
private static String wordEachBigFs(String str){
|
||||
return wordFirstSmall(wordEachBig(str));
|
||||
}
|
||||
|
||||
/** 将指定单词首字母大写 */
|
||||
private static String wordFirstBig(String str) {
|
||||
return str.substring(0, 1).toUpperCase() + str.substring(1, str.length());
|
||||
}
|
||||
|
||||
/** 将指定单词首字母小写 */
|
||||
private static String wordFirstSmall(String str) {
|
||||
return str.substring(0, 1).toLowerCase() + str.substring(1, str.length());
|
||||
}
|
||||
|
||||
/** 下划线转中划线 */
|
||||
private static String wordEachKebabCase(String str) {
|
||||
return str.replaceAll("_", "-");
|
||||
}
|
||||
|
||||
/** 驼峰转下划线 */
|
||||
private static String wordHumpToLine(String str) {
|
||||
return str.replaceAll("[A-Z]", "_$0").toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
server:
|
||||
port: 8001
|
||||
|
||||
spring:
|
||||
# 静态文件路径映射
|
||||
resources:
|
||||
static-locations: classpath:/META-INF/resources/,classpath:/resources/, classpath:/static/, classpath:/public/
|
||||
# static-locations: file:E:\work\project-yun\sa-token\sa-token-demo-oauth2\sa-token-demo-oauth2-server\src\main\resources\static\
|
||||
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# token名称 (同时也是cookie名称)
|
||||
token-name: satoken-server
|
||||
|
||||
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 1
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
# password:
|
||||
# 连接超时时间(毫秒)
|
||||
timeout: 1000ms
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
max-active: 200
|
||||
# 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 10
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,93 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>服务提供方-确认授权页</title>
|
||||
<style type="text/css">
|
||||
*{margin: 0px; padding: 0px;}
|
||||
.login-box{width: 300px; margin: 50px auto; padding: 100px; border: 1px #000 solid;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-box">
|
||||
<h2>服务提供方-确认授权页</h2> <br>
|
||||
<div>
|
||||
<div>应用id: <span class="client_id"></span></div>
|
||||
<div>请求授权: <span class="scope"></span></div>
|
||||
<br>
|
||||
<div>是否同意授权: </div>
|
||||
<div>
|
||||
<button onclick="ok()">同意</button>
|
||||
<button onclick="no()">拒绝</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
// 获取code详细信息
|
||||
var code = getParam("code", null);
|
||||
var codeObj = null;
|
||||
if(code == null) {
|
||||
throw alert("无效code");
|
||||
}
|
||||
$.ajax({
|
||||
url: '/oauth2/getCodeInfo',
|
||||
data: {code: code},
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
if(res.data == null) {
|
||||
return alert('无效授权码');
|
||||
}
|
||||
codeObj = res.data;
|
||||
$(".client_id").html(res.data.clientId);
|
||||
$(".scope").html(res.data.scope);
|
||||
},
|
||||
error: function(e, ee, eee) {
|
||||
console.log('error');
|
||||
alert(eee);
|
||||
}
|
||||
});
|
||||
|
||||
// 确认授权
|
||||
function ok() {
|
||||
$.ajax({
|
||||
url: '/oauth2/confirm',
|
||||
data: {code: code},
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
if(res.code == 200) {
|
||||
// 跳转
|
||||
location.href = codeObj.redirectUri;
|
||||
} else {
|
||||
alert(res.msg);
|
||||
}
|
||||
},
|
||||
error: function(e, ee, eee) {
|
||||
alert(eee);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 拒绝授权时,跳入拒绝授权地址
|
||||
function no() {
|
||||
// alert(codeObj.rejectUri)
|
||||
location.href = codeObj.rejectUri;
|
||||
}
|
||||
|
||||
// 从url中查询到指定名称的参数值
|
||||
function getParam(name, defaultValue){
|
||||
var query = window.location.search.substring(1);
|
||||
var vars = query.split("&");
|
||||
for (var i=0;i<vars.length;i++) {
|
||||
var pair = vars[i].split("=");
|
||||
if(pair[0] == name){return pair[1];}
|
||||
}
|
||||
return(defaultValue == undefined ? null : defaultValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>服务提供方-登录页</title>
|
||||
<style type="text/css">
|
||||
*{margin: 0px; padding: 0px;}
|
||||
.login-box{width: 400px; margin: 50px auto;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-box">
|
||||
<h2>服务提供方-登录页</h2> <br>
|
||||
<div>注:您当前在服务提供方尚未登录,请先登录</div>
|
||||
<div>测试账号: test test</div> <br>
|
||||
账号:<input name="username" /> <br>
|
||||
密码:<input name="password" type="password" /> <br>
|
||||
<button onclick="doLogin()">登录</button>
|
||||
</div>
|
||||
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
// 登录方法
|
||||
function getLoginId() {
|
||||
|
||||
}
|
||||
|
||||
// 登录方法
|
||||
function doLogin() {
|
||||
console.log('-----------');
|
||||
$.ajax({
|
||||
url: '/doLogin',
|
||||
data: {
|
||||
username: $('[name=username]').val(),
|
||||
password: $('[name=password]').val()
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
if(res.code == 200) {
|
||||
alert('登录成功');
|
||||
location.reload(true);
|
||||
} else {
|
||||
alert('登录失败');
|
||||
}
|
||||
},
|
||||
error: function(e) {
|
||||
console.log('error');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
13
sa-token-oauth2/.gitignore
vendored
Normal file
13
sa-token-oauth2/.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.factorypath
|
||||
|
||||
.idea/
|
||||
.iml
|
11
sa-token-oauth2/README.md
Normal file
11
sa-token-oauth2/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# sa-token-oauth2 内测版
|
||||
|
||||
sa-token-oauth2 模块是 sa-token 实现 oauth2.0 的部分,目前该模块功能完成度较低,为避免不可预知的风险,建议仅做学习测试使用
|
||||
|
||||
## 启动步骤
|
||||
|
||||
1. 启动项目 `sa-token-demo-oauth2-server`, 此为OAuth2.0的服务提供方
|
||||
2. 启动项目 `sa-token-demo-oauth2-client`, 此为OAuth2.0的客户端
|
||||
3. 根据控制台打印,访问测试地址即可:[http://localhost:8002/login.html](http://localhost:8002/login.html)
|
||||
|
||||
可结合代码注释学习查看
|
30
sa-token-oauth2/pom.xml
Normal file
30
sa-token-oauth2/pom.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.13.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>sa-token-dao-redis</name>
|
||||
<artifactId>sa-token-oauth2</artifactId>
|
||||
<version>1.13.0-alpha</version>
|
||||
<description>sa-token realization oauth2.0</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- sa-token-core -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-core</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
</project>
|
@ -0,0 +1,54 @@
|
||||
package cn.dev33.satoken.oauth2;
|
||||
|
||||
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
|
||||
import cn.dev33.satoken.oauth2.logic.SaOAuth2Interface;
|
||||
import cn.dev33.satoken.oauth2.logic.SaOAuth2InterfaceDefaultImpl;
|
||||
|
||||
/**
|
||||
* sa-token oauth2 模块 总控类
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaOAuth2Manager {
|
||||
|
||||
/**
|
||||
* OAuth2 配置 Bean
|
||||
*/
|
||||
private static SaOAuth2Config config;
|
||||
public static SaOAuth2Config getConfig() {
|
||||
if (config == null) {
|
||||
// 初始化默认值
|
||||
synchronized (SaOAuth2Manager.class) {
|
||||
if (config == null) {
|
||||
setConfig(new SaOAuth2Config());
|
||||
}
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
public static void setConfig(SaOAuth2Config config) {
|
||||
SaOAuth2Manager.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* sa-token-oauth2 逻辑 Bean
|
||||
*/
|
||||
private static SaOAuth2Interface saOAuth2Interface;
|
||||
public static SaOAuth2Interface getInterface() {
|
||||
if (saOAuth2Interface == null) {
|
||||
// 初始化默认值
|
||||
synchronized (SaOAuth2Manager.class) {
|
||||
if (saOAuth2Interface == null) {
|
||||
setInterface(new SaOAuth2InterfaceDefaultImpl());
|
||||
}
|
||||
}
|
||||
}
|
||||
return saOAuth2Interface;
|
||||
}
|
||||
public static void setInterface(SaOAuth2Interface interfaceObj) {
|
||||
SaOAuth2Manager.saOAuth2Interface = interfaceObj;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package cn.dev33.satoken.oauth2.config;
|
||||
|
||||
/**
|
||||
* sa-token oauth2 配置类 Model
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaOAuth2Config {
|
||||
|
||||
/**
|
||||
* 授权码默认保存的时间(单位秒) 默认五分钟
|
||||
*/
|
||||
private long codeTimeout = 60 * 5;
|
||||
|
||||
/**
|
||||
* access_token默认保存的时间(单位秒) 默认两个小时
|
||||
*/
|
||||
private long accessTokenTimeout = 60 * 60 * 2;
|
||||
|
||||
/**
|
||||
* refresh_token默认保存的时间(单位秒) 默认30 天
|
||||
*/
|
||||
private long refreshTokenTimeout = 60 * 60 * 24 * 30;
|
||||
|
||||
|
||||
/**
|
||||
* @return codeTimeout
|
||||
*/
|
||||
public long getCodeTimeout() {
|
||||
return codeTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param codeTimeout 要设置的 codeTimeout
|
||||
*/
|
||||
public void setCodeTimeout(long codeTimeout) {
|
||||
this.codeTimeout = codeTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return accessTokenTimeout
|
||||
*/
|
||||
public long getAccessTokenTimeout() {
|
||||
return accessTokenTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param accessTokenTimeout 要设置的 accessTokenTimeout
|
||||
*/
|
||||
public void setAccessTokenTimeout(long accessTokenTimeout) {
|
||||
this.accessTokenTimeout = accessTokenTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return refreshTokenTimeout
|
||||
*/
|
||||
public long getRefreshTokenTimeout() {
|
||||
return refreshTokenTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param refreshTokenTimeout 要设置的 refreshTokenTimeout
|
||||
*/
|
||||
public void setRefreshTokenTimeout(long refreshTokenTimeout) {
|
||||
this.refreshTokenTimeout = refreshTokenTimeout;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,553 @@
|
||||
package cn.dev33.satoken.oauth2.logic;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
|
||||
import cn.dev33.satoken.oauth2.model.AccessTokenModel;
|
||||
import cn.dev33.satoken.oauth2.model.CodeModel;
|
||||
import cn.dev33.satoken.oauth2.model.RequestAuthModel;
|
||||
import cn.dev33.satoken.oauth2.util.SaOAuth2Consts;
|
||||
import cn.dev33.satoken.oauth2.util.SaOAuth2InsideUtil;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* sa-token-oauth2 模块 逻辑接口
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaOAuth2Interface {
|
||||
|
||||
|
||||
// ------------------- 获取数据
|
||||
|
||||
/**
|
||||
* [default] 返回此平台所有权限集合
|
||||
* @return 此平台所有权限名称集合
|
||||
*/
|
||||
public default List<String> getAppScopeList() {
|
||||
return Arrays.asList("userinfo");
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 返回指定Client签约的所有Scope名称集合
|
||||
* @param clientId 应用id
|
||||
* @return Scope集合
|
||||
*/
|
||||
public default List<String> getClientScopeList(String clientId) {
|
||||
// 默认返回此APP的所有权限
|
||||
return getAppScopeList();
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 获取指定 LoginId 对指定 Client 已经授权过的所有 Scope
|
||||
* @param clientId 应用id
|
||||
* @param loginId 账号id
|
||||
* @return Scope集合
|
||||
*/
|
||||
public default List<String> getGrantScopeList(Object loginId, String clientId) {
|
||||
// 默认返回空集合
|
||||
return Arrays.asList();
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 返回指定Client允许的回调域名, 多个用逗号隔开, *代表不限制
|
||||
* @param clientId 应用id
|
||||
* @return domain集合
|
||||
*/
|
||||
public default String getClientDomain(String clientId) {
|
||||
return "*";
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 返回指定ClientId的ClientSecret
|
||||
* @param clientId 应用id
|
||||
* @return 此应用的秘钥
|
||||
*/
|
||||
public default String getClientSecret(String clientId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 根据ClientId和LoginId返回openid
|
||||
* @param clientId 应用id
|
||||
* @param loginId 账号id
|
||||
* @return 此账号在此Client下的openid
|
||||
*/
|
||||
public default String getOpenid(String clientId, Object loginId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 根据ClientId和openid返回LoginId
|
||||
* @param clientId 应用id
|
||||
* @param openid openid
|
||||
* @return LoginId
|
||||
*/
|
||||
public default Object getLoginId(String clientId, String openid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 数据校验
|
||||
|
||||
/**
|
||||
* [default] 检查一个 Client 是否签约了指定的Scope
|
||||
* @param clientId 应用id
|
||||
* @param scope 权限
|
||||
*/
|
||||
public default void checkContract(String clientId, String scope) {
|
||||
List<String> clientScopeList = getClientScopeList(clientId);
|
||||
List<String> scopelist = Arrays.asList(scope.split(","));
|
||||
if(clientScopeList.containsAll(scopelist) == false) {
|
||||
throw new SaTokenException("请求授权范围超出或无效");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 指定 loginId 是否对一个 Client 授权给了指定 Scope
|
||||
* @param loginId 账号id
|
||||
* @param clientId 应用id
|
||||
* @param scope 权限
|
||||
* @return 是否已经授权
|
||||
*/
|
||||
public default boolean isGrant(Object loginId, String clientId, String scope) {
|
||||
List<String> grantScopeList = getGrantScopeList(loginId, clientId);
|
||||
List<String> scopeList = convertStringToList(scope);
|
||||
return grantScopeList.containsAll(scopeList);
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 指定Client使用指定url作为回调地址,是否合法
|
||||
* @param clientId 应用id
|
||||
* @param url 指定url
|
||||
*/
|
||||
public default void checkRightUrl(String clientId, String url) {
|
||||
// 首先检测url格式
|
||||
if(SaOAuth2InsideUtil.isUrl(url) == false) {
|
||||
throw new SaTokenException("url格式错误");
|
||||
}
|
||||
// ---- 检测
|
||||
|
||||
// 获取此应用允许的域名列表
|
||||
String domain = getClientDomain(clientId);
|
||||
// 如果是null或者空字符串, 则代表任何域名都无法通过检查
|
||||
if(domain == null || "".equals(domain)) {
|
||||
throw new SaTokenException("重定向地址无效");
|
||||
}
|
||||
// 如果是*符号,代表允许任何域名
|
||||
if(SaOAuth2Consts.UNLIMITED_DOMAIN.equals(domain)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取域名进行比对
|
||||
try {
|
||||
String host = new URL(url).getHost();
|
||||
List<String> domainList = convertStringToList(domain);
|
||||
if(domainList.contains(host) == false) {
|
||||
throw new SaTokenException("重定向地址不在列表中");
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
throw new SaTokenException("url格式错误", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 校验code、clientId、clientSecret 三者是否正确
|
||||
* @param code 授权码
|
||||
* @param clientId 应用id
|
||||
* @param clientSecret 秘钥
|
||||
* @return CodeModel对象
|
||||
*/
|
||||
public default CodeModel checkCodeIdSecret(String code, String clientId, String clientSecret) {
|
||||
|
||||
// 获取授权码信息
|
||||
CodeModel codeModel = getCode(code);
|
||||
|
||||
// 验证code、client_id、client_secret
|
||||
if(codeModel == null) {
|
||||
throw new SaTokenException("无效code");
|
||||
}
|
||||
|
||||
if(codeModel.getClientId().equals(clientId) == false){
|
||||
throw new SaTokenException("无效client_id");
|
||||
}
|
||||
String dbClientSecret = getClientSecret(clientId);
|
||||
System.out.println(dbClientSecret);
|
||||
System.out.println(clientSecret);
|
||||
if(dbClientSecret == null || dbClientSecret.equals(clientSecret) == false){
|
||||
throw new SaTokenException("无效client_secret");
|
||||
}
|
||||
|
||||
// 返回CodeMdoel
|
||||
return codeModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 校验access_token、clientId、clientSecret 三者是否正确
|
||||
* @param accessToken access_token
|
||||
* @param clientId 应用id
|
||||
* @param clientSecret 秘钥
|
||||
* @return AccessTokenModel对象
|
||||
*/
|
||||
public default AccessTokenModel checkTokenIdSecret(String accessToken, String clientId, String clientSecret) {
|
||||
|
||||
// 获取授权码信息
|
||||
AccessTokenModel tokenModel = getAccessToken(accessToken);
|
||||
|
||||
// 验证code、client_id、client_secret
|
||||
if(tokenModel == null) {
|
||||
throw new SaTokenException("无效access_token");
|
||||
}
|
||||
if(tokenModel.getClientId().equals(clientId) == false){
|
||||
throw new SaTokenException("无效client_id");
|
||||
}
|
||||
String dbClientSecret = getClientSecret(clientId);
|
||||
if(dbClientSecret == null || dbClientSecret.equals(clientSecret)){
|
||||
throw new SaTokenException("无效client_secret");
|
||||
}
|
||||
|
||||
// 返回AccessTokenModel
|
||||
return tokenModel;
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 逻辑相关
|
||||
|
||||
// ---- 授权码
|
||||
/**
|
||||
* [default] 根据参数生成一个授权码并返回
|
||||
* @param authModel 请求授权参数Model
|
||||
* @return 授权码Model
|
||||
*/
|
||||
public default CodeModel generateCode(RequestAuthModel authModel) {
|
||||
|
||||
// 获取参数
|
||||
String clientId = authModel.getClientId();
|
||||
String scope = authModel.getScope();
|
||||
Object loginId = authModel.getLoginId();
|
||||
String redirectUri = authModel.getRedirectUri();
|
||||
String state = authModel.getState();
|
||||
|
||||
// ------ 参数校验
|
||||
// 此Client是否签约了此Scope
|
||||
checkContract(clientId, scope);
|
||||
|
||||
// 校验重定向域名的格式是否合法
|
||||
checkRightUrl(clientId, redirectUri);
|
||||
|
||||
// ------ 开始生成code码
|
||||
String code = createCode(clientId, scope, loginId);
|
||||
CodeModel codeModel = new CodeModel(code, clientId, scope, loginId);
|
||||
|
||||
// 拼接授权后重定向的域名 (拼接code和state参数)
|
||||
String url = splicingParame(redirectUri, "code=" + code);
|
||||
if(state != null) {
|
||||
url = splicingParame(url, "state=" + state);
|
||||
}
|
||||
codeModel.setRedirectUri(url);
|
||||
|
||||
// 拒绝授权时重定向的地址
|
||||
codeModel.setRejectUri(splicingParame(redirectUri, "handle=reject"));
|
||||
|
||||
// 计算此Scope是否已经授权过了
|
||||
codeModel.setIsConfirm(isGrant(loginId, clientId, scope));
|
||||
|
||||
// ------ 开始保存
|
||||
|
||||
// 将此授权码保存到DB
|
||||
long codeTimeout = SaOAuth2Manager.getConfig().getCodeTimeout();
|
||||
SaTokenManager.getSaTokenDao().setObject(getKeyCodeModel(code), codeModel, codeTimeout);
|
||||
|
||||
// 如果此[Client&账号]已经有code正在存储,则先删除它
|
||||
String key = getKeyClientLoginId(loginId, clientId);
|
||||
SaTokenManager.getSaTokenDao().delete(key);
|
||||
|
||||
// 将此[Client&账号]的最新授权码保存到DB中
|
||||
// 以便于完成授权码覆盖操作: 保证每次只有最新的授权码有效
|
||||
SaTokenManager.getSaTokenDao().set(key, code, codeTimeout);
|
||||
|
||||
// 返回
|
||||
return codeModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 根据授权码获得授权码Model
|
||||
* @param code 授权码
|
||||
* @return 授权码Model
|
||||
*/
|
||||
public default CodeModel getCode(String code) {
|
||||
return (CodeModel)SaTokenManager.getSaTokenDao().getObject(getKeyCodeModel(code));
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 手动更改授权码对象信息
|
||||
* @param code 授权码
|
||||
* @param codeModel 授权码Model
|
||||
*/
|
||||
public default void updateCode(String code, CodeModel codeModel) {
|
||||
SaTokenManager.getSaTokenDao().updateObject(getKeyCodeModel(code), codeModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 确认授权一个code
|
||||
* @param code 授权码
|
||||
*/
|
||||
public default void confirmCode(String code) {
|
||||
// 获取codeModel
|
||||
CodeModel codeModel = getCode(code);
|
||||
// 如果该code码已经确认
|
||||
if(codeModel.getIsConfirm() == true) {
|
||||
return;
|
||||
}
|
||||
// 进行确认
|
||||
codeModel.setIsConfirm(true);
|
||||
updateCode(code, codeModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 删除一个授权码
|
||||
* @param code 授权码
|
||||
*/
|
||||
public default void deleteCode(String code) {
|
||||
SaTokenManager.getSaTokenDao().deleteObject(getKeyCodeModel(code));
|
||||
}
|
||||
|
||||
|
||||
// ------------------- access_token 和 refresh_token 相关
|
||||
|
||||
/**
|
||||
* [default] 根据授权码Model生成一个access_token
|
||||
* @param codeModel 授权码Model
|
||||
* @return AccessTokenModel
|
||||
*/
|
||||
public default AccessTokenModel generateAccessToken(CodeModel codeModel) {
|
||||
|
||||
// 先校验
|
||||
if(codeModel == null) {
|
||||
throw new SaTokenException("无效code");
|
||||
}
|
||||
if(codeModel.getIsConfirm() == false) {
|
||||
throw new SaTokenException("该code尚未授权");
|
||||
}
|
||||
|
||||
// 获取 TokenModel 并保存
|
||||
AccessTokenModel tokenModel = converCodeToAccessToken(codeModel);
|
||||
SaTokenManager.getSaTokenDao().setObject(getKeyAccessToken(tokenModel.getAccessToken()), tokenModel, SaOAuth2Manager.getConfig().getAccessTokenTimeout());
|
||||
|
||||
// 将此 CodeModel 当做 refresh_token 保存下来
|
||||
SaTokenManager.getSaTokenDao().setObject(getKeyRefreshToken(tokenModel.getRefreshToken()), codeModel, SaOAuth2Manager.getConfig().getRefreshTokenTimeout());
|
||||
|
||||
// 返回
|
||||
return tokenModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 根据 access_token 获得其Model详细信息
|
||||
* @param accessToken access_token
|
||||
* @return AccessTokenModel (授权码Model)
|
||||
*/
|
||||
public default AccessTokenModel getAccessToken(String accessToken) {
|
||||
return (AccessTokenModel)SaTokenManager.getSaTokenDao().getObject(getKeyAccessToken(accessToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 根据 refresh_token 生成一个新的 access_token
|
||||
* @param refreshToken refresh_token
|
||||
* @return 新的 access_token
|
||||
*/
|
||||
public default AccessTokenModel refreshAccessToken(String refreshToken) {
|
||||
// 获取Model信息
|
||||
CodeModel codeModel = getRefreshToken(refreshToken);
|
||||
if(codeModel == null) {
|
||||
throw new SaTokenException("无效refresh_token");
|
||||
}
|
||||
// 获取新的 AccessToken 并保存
|
||||
AccessTokenModel tokenModel = converCodeToAccessToken(codeModel);
|
||||
SaTokenManager.getSaTokenDao().setObject(getKeyAccessToken(tokenModel.getAccessToken()), tokenModel, SaOAuth2Manager.getConfig().getAccessTokenTimeout());
|
||||
|
||||
// 返回
|
||||
return tokenModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 根据 refresh_token 获得其Model详细信息 (授权码Model)
|
||||
* @param refreshToken refresh_token
|
||||
* @return RefreshToken (授权码Model)
|
||||
*/
|
||||
public default CodeModel getRefreshToken(String refreshToken) {
|
||||
return (CodeModel)SaTokenManager.getSaTokenDao().getObject(getKeyRefreshToken(refreshToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 获取 access_token 的有效期
|
||||
* @param accessToken access_token
|
||||
* @return 有效期
|
||||
*/
|
||||
public default long getAccessTokenExpiresIn(String accessToken) {
|
||||
return SaTokenManager.getSaTokenDao().getObjectTimeout(getKeyAccessToken(accessToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 获取 refresh_token 的有效期
|
||||
* @param refreshToken refresh_token
|
||||
* @return 有效期
|
||||
*/
|
||||
public default long getRefreshTokenExpiresIn(String refreshToken) {
|
||||
return SaTokenManager.getSaTokenDao().getObjectTimeout(getKeyRefreshToken(refreshToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 获取 access_token 所代表的LoginId
|
||||
* @param accessToken access_token
|
||||
* @return LoginId
|
||||
*/
|
||||
public default Object getLoginIdByAccessToken(String accessToken) {
|
||||
AccessTokenModel tokenModel = SaOAuth2Util.getAccessToken(accessToken);
|
||||
if(tokenModel == null) {
|
||||
throw new SaTokenException("无效access_token");
|
||||
}
|
||||
return getLoginId(tokenModel.getClientId(), tokenModel.getOpenid());
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 自定义策略相关
|
||||
|
||||
/**
|
||||
* [default] 将指定字符串按照逗号分隔符转化为字符串集合
|
||||
* @param str 字符串
|
||||
* @return 分割后的字符串集合
|
||||
*/
|
||||
public default List<String> convertStringToList(String str) {
|
||||
return Arrays.asList(str.split(","));
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 生成授权码
|
||||
* @param clientId 应用id
|
||||
* @param scope 权限
|
||||
* @param loginId 账号id
|
||||
* @return 授权码
|
||||
*/
|
||||
public default String createCode(String clientId, String scope, Object loginId) {
|
||||
return SaTokenInsideUtil.getRandomString(60).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 生成AccessToken
|
||||
* @param codeModel CodeModel对象
|
||||
* @return AccessToken
|
||||
*/
|
||||
public default String createAccessToken(CodeModel codeModel) {
|
||||
return SaTokenInsideUtil.getRandomString(60).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 生成RefreshToken
|
||||
* @param codeModel CodeModel对象
|
||||
* @return RefreshToken
|
||||
*/
|
||||
public default String createRefreshToken(CodeModel codeModel) {
|
||||
return SaTokenInsideUtil.getRandomString(60).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 在url上拼接上kv参数并返回
|
||||
* @param url url
|
||||
* @param parameStr 参数, 例如 id=1001
|
||||
* @return 拼接后的url字符串
|
||||
*/
|
||||
public default String splicingParame(String url, String parameStr) {
|
||||
// 如果参数为空, 直接返回
|
||||
if(parameStr == null || parameStr.length() == 0) {
|
||||
return url;
|
||||
}
|
||||
int index = url.indexOf('?');
|
||||
// ? 不存在
|
||||
if(index == -1) {
|
||||
return url + '?' + parameStr;
|
||||
}
|
||||
// ? 是最后一位
|
||||
if(index == url.length() - 1) {
|
||||
return url + parameStr;
|
||||
}
|
||||
// ? 是其中一位
|
||||
if(index > -1 && index < url.length() - 1) {
|
||||
String separatorChar = "&";
|
||||
// 如果最后一位是 不是&, 且 arg_str 第一位不是 &, 就增送一个 &
|
||||
if(url.lastIndexOf(separatorChar) != url.length() - 1 && parameStr.indexOf(separatorChar) != 0) {
|
||||
return url + separatorChar + parameStr;
|
||||
} else {
|
||||
return url + parameStr;
|
||||
}
|
||||
}
|
||||
// 正常情况下, 代码不可能执行到此
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 将 CodeModel 转换为 AccessTokenModel
|
||||
* @param codeModel CodeModel对象
|
||||
* @return AccessToken对象
|
||||
*/
|
||||
public default AccessTokenModel converCodeToAccessToken(CodeModel codeModel) {
|
||||
if(codeModel == null) {
|
||||
throw new SaTokenException("无效code");
|
||||
}
|
||||
AccessTokenModel tokenModel = new AccessTokenModel();
|
||||
tokenModel.setAccessToken(createAccessToken(codeModel));
|
||||
tokenModel.setRefreshToken(createRefreshToken(codeModel));
|
||||
tokenModel.setCode(codeModel.getCode());
|
||||
tokenModel.setClientId(codeModel.getClientId());
|
||||
tokenModel.setScope(codeModel.getScope());
|
||||
tokenModel.setOpenid(getOpenid(codeModel.getClientId(), codeModel.getLoginId()));
|
||||
tokenModel.setTag(codeModel.getTag());
|
||||
return tokenModel;
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 返回相应key
|
||||
|
||||
/**
|
||||
* 获取key:授权码持久化使用的key
|
||||
* @param code 授权码
|
||||
* @return key
|
||||
*/
|
||||
public default String getKeyCodeModel(String code) {
|
||||
return SaTokenManager.getConfig().getTokenName() + ":oauth2:code:" + code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取key:[Client&账号]最新授权码记录, 持久化使用的key
|
||||
* @param loginId 账号id
|
||||
* @param clientId 应用id
|
||||
* @return key
|
||||
*/
|
||||
public default String getKeyClientLoginId(Object loginId, String clientId) {
|
||||
return SaTokenManager.getConfig().getTokenName() + ":oauth2:newest-code:" + clientId + ":" + loginId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取key:refreshToken持久化使用的key
|
||||
* @param refreshToken refreshToken
|
||||
* @return key
|
||||
*/
|
||||
public default String getKeyRefreshToken(String refreshToken) {
|
||||
return SaTokenManager.getConfig().getTokenName() + ":oauth2:refresh-token:" + refreshToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取key:accessToken持久化使用的key
|
||||
* @param accessToken accessToken
|
||||
* @return key
|
||||
*/
|
||||
public default String getKeyAccessToken(String accessToken) {
|
||||
return SaTokenManager.getConfig().getTokenName() + ":oauth2:access-token:" + accessToken;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package cn.dev33.satoken.oauth2.logic;
|
||||
|
||||
/**
|
||||
* SaOAuth2Interface 默认实现类 (只构建userinfo单个权限)
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaOAuth2InterfaceDefaultImpl implements SaOAuth2Interface {
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,197 @@
|
||||
package cn.dev33.satoken.oauth2.logic;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
|
||||
import cn.dev33.satoken.oauth2.model.AccessTokenModel;
|
||||
import cn.dev33.satoken.oauth2.model.CodeModel;
|
||||
import cn.dev33.satoken.oauth2.model.RequestAuthModel;
|
||||
|
||||
/**
|
||||
* sa-token-oauth2 模块 静态类接口转发, 方便调用
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaOAuth2Util {
|
||||
|
||||
// ------------------- 获取数据
|
||||
|
||||
/**
|
||||
* 返回此平台所有权限集合
|
||||
* @return 此平台所有权限名称集合
|
||||
*/
|
||||
public static List<String> getAppScopeList() {
|
||||
return SaOAuth2Manager.getInterface().getAppScopeList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回指定Client签约的所有Scope名称集合
|
||||
* @param clientId 应用id
|
||||
* @return Scope集合
|
||||
*/
|
||||
public static List<String> getClientScopeList(String clientId) {
|
||||
return SaOAuth2Manager.getInterface().getClientScopeList(clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 LoginId 对指定 Client 已经授权过的所有 Scope
|
||||
* @param clientId 应用id
|
||||
* @param loginId 账号id
|
||||
* @return Scope集合
|
||||
*/
|
||||
public static List<String> getGrantScopeList(Object loginId, String clientId) {
|
||||
return SaOAuth2Manager.getInterface().getGrantScopeList(loginId, clientId);
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 数据校验
|
||||
|
||||
/**
|
||||
* 指定 loginId 是否对一个 Client 授权给了指定 Scope
|
||||
* @param clientId 应用id
|
||||
* @param scope 权限
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public static boolean isGrant(Object loginId, String clientId, String scope) {
|
||||
return SaOAuth2Manager.getInterface().isGrant(loginId, clientId, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验code、clientId、clientSecret 三者是否正确
|
||||
* @param code 授权码
|
||||
* @param clientId 应用id
|
||||
* @param clientSecret 秘钥
|
||||
* @return CodeModel对象
|
||||
*/
|
||||
public static CodeModel checkCodeIdSecret(String code, String clientId, String clientSecret) {
|
||||
return SaOAuth2Manager.getInterface().checkCodeIdSecret(code, clientId, clientSecret);
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 校验access_token、clientId、clientSecret 三者是否正确
|
||||
* @param accessToken access_token
|
||||
* @param clientId 应用id
|
||||
* @param clientSecret 秘钥
|
||||
* @return AccessTokenModel对象
|
||||
*/
|
||||
public static AccessTokenModel checkTokenIdSecret(String accessToken, String clientId, String clientSecret) {
|
||||
return SaOAuth2Manager.getInterface().checkTokenIdSecret(accessToken, clientId, clientSecret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ------------------- 逻辑相关
|
||||
|
||||
/**
|
||||
* 根据参数生成一个授权码并返回
|
||||
* @param authModel 请求授权参数Model
|
||||
* @return 授权码Model
|
||||
*/
|
||||
public static CodeModel generateCode(RequestAuthModel authModel) {
|
||||
return SaOAuth2Manager.getInterface().generateCode(authModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据授权码获得授权码Model
|
||||
* @param code 授权码
|
||||
* @return 授权码Model
|
||||
*/
|
||||
public static CodeModel getCode(String code) {
|
||||
return SaOAuth2Manager.getInterface().getCode(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动更改授权码对象信息
|
||||
* @param code 授权码
|
||||
* @param codeModel 授权码Model
|
||||
*/
|
||||
public static void updateCode(String code, CodeModel codeModel) {
|
||||
SaOAuth2Manager.getInterface().updateCode(code, codeModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认授权一个code
|
||||
* @param code 授权码
|
||||
*/
|
||||
public static void confirmCode(String code) {
|
||||
SaOAuth2Manager.getInterface().confirmCode(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 删除一个授权码
|
||||
* @param code 授权码
|
||||
*/
|
||||
public static void deleteCode(String code) {
|
||||
SaOAuth2Manager.getInterface().deleteCode(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 根据授权码Model生成一个access_token
|
||||
* @param codeModel 授权码Model
|
||||
* @return AccessTokenModel
|
||||
*/
|
||||
public static AccessTokenModel generateAccessToken(CodeModel codeModel) {
|
||||
return SaOAuth2Manager.getInterface().generateAccessToken(codeModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 根据 access_token 获得其Model详细信息
|
||||
* @param accessToken access_token
|
||||
* @return AccessTokenModel (授权码Model)
|
||||
*/
|
||||
public static AccessTokenModel getAccessToken(String accessToken) {
|
||||
return SaOAuth2Manager.getInterface().getAccessToken(accessToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 refresh_token 生成一个新的 access_token
|
||||
* @param refreshToken refresh_token
|
||||
* @return 新的 access_token
|
||||
*/
|
||||
public static AccessTokenModel refreshAccessToken(String refreshToken) {
|
||||
return SaOAuth2Manager.getInterface().refreshAccessToken(refreshToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 根据 refresh_token 获得其Model详细信息 (授权码Model)
|
||||
* @param refreshToken refresh_token
|
||||
* @return RefreshToken (授权码Model)
|
||||
*/
|
||||
public static CodeModel getRefreshToken(String refreshToken) {
|
||||
return SaOAuth2Manager.getInterface().getRefreshToken(refreshToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 获取 access_token 的有效期
|
||||
* @param accessToken access_token
|
||||
* @return 有效期
|
||||
*/
|
||||
public static long getAccessTokenExpiresIn(String accessToken) {
|
||||
return SaOAuth2Manager.getInterface().getAccessTokenExpiresIn(accessToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 获取 refresh_token 的有效期
|
||||
* @param refreshToken refresh_token
|
||||
* @return 有效期
|
||||
*/
|
||||
public static long getRefreshTokenExpiresIn(String refreshToken) {
|
||||
return SaOAuth2Manager.getInterface().getRefreshTokenExpiresIn(refreshToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* [default] 获取 access_token 所代表的LoginId
|
||||
* @param accessToken access_token
|
||||
* @return LoginId
|
||||
*/
|
||||
public static Object getLoginIdByAccessToken(String accessToken) {
|
||||
return SaOAuth2Manager.getInterface().getLoginIdByAccessToken(accessToken);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,186 @@
|
||||
package cn.dev33.satoken.oauth2.model;
|
||||
|
||||
/**
|
||||
* Model: access_token
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class AccessTokenModel {
|
||||
|
||||
/**
|
||||
* access_token 值
|
||||
*/
|
||||
private String accessToken;
|
||||
|
||||
/**
|
||||
* refresh_token 值
|
||||
*/
|
||||
private String refreshToken;
|
||||
|
||||
/**
|
||||
* access_token 剩余有效时间 (秒)
|
||||
*/
|
||||
private long expiresIn;
|
||||
|
||||
/**
|
||||
* refresh_token 剩余有效期 (秒)
|
||||
*/
|
||||
private long refreshExpiresIn;
|
||||
|
||||
/**
|
||||
* 此 access_token令牌 是由哪个code码创建
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 应用id
|
||||
*/
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* 授权范围
|
||||
*/
|
||||
private String scope;
|
||||
|
||||
/**
|
||||
* 开放账号id
|
||||
*/
|
||||
private String openid;
|
||||
|
||||
/**
|
||||
* 其他自定义数据
|
||||
*/
|
||||
private Object tag;
|
||||
|
||||
|
||||
/**
|
||||
* @return accessToken
|
||||
*/
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param accessToken 要设置的 accessToken
|
||||
*/
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return refreshToken
|
||||
*/
|
||||
public String getRefreshToken() {
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param refreshToken 要设置的 refreshToken
|
||||
*/
|
||||
public void setRefreshToken(String refreshToken) {
|
||||
this.refreshToken = refreshToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return expiresIn
|
||||
*/
|
||||
public long getExpiresIn() {
|
||||
return expiresIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param expiresIn 要设置的 expiresIn
|
||||
*/
|
||||
public void setExpiresIn(long expiresIn) {
|
||||
this.expiresIn = expiresIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return refreshExpiresIn
|
||||
*/
|
||||
public long getRefreshExpiresIn() {
|
||||
return refreshExpiresIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param refreshExpiresIn 要设置的 refreshExpiresIn
|
||||
*/
|
||||
public void setRefreshExpiresIn(long refreshExpiresIn) {
|
||||
this.refreshExpiresIn = refreshExpiresIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return code
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param code 要设置的 code
|
||||
*/
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return clientId
|
||||
*/
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clientId 要设置的 clientId
|
||||
*/
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return scope
|
||||
*/
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scope 要设置的 scope
|
||||
*/
|
||||
public void setScope(String scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return openid
|
||||
*/
|
||||
public String getOpenid() {
|
||||
return openid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param openid 要设置的 openid
|
||||
*/
|
||||
public void setOpenid(String openid) {
|
||||
this.openid = openid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return tag
|
||||
*/
|
||||
public Object getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tag 要设置的 tag
|
||||
*/
|
||||
public void setTag(Object tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
package cn.dev33.satoken.oauth2.model;
|
||||
|
||||
/**
|
||||
* Model: [授权码 - 数据 对应关系]
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class CodeModel {
|
||||
|
||||
/**
|
||||
* 授权码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 应用id
|
||||
*/
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* 授权范围
|
||||
*/
|
||||
private String scope;
|
||||
|
||||
/**
|
||||
* 对应账号id
|
||||
*/
|
||||
private Object loginId;
|
||||
|
||||
/**
|
||||
* 用户是否已经确认了这个授权
|
||||
*/
|
||||
private Boolean isConfirm;
|
||||
|
||||
/**
|
||||
* 确认授权后重定向的地址
|
||||
*/
|
||||
private String redirectUri;
|
||||
|
||||
/**
|
||||
* 拒绝授权后重定向的地址
|
||||
*/
|
||||
private String rejectUri;
|
||||
|
||||
|
||||
/**
|
||||
* 其他自定义数据
|
||||
*/
|
||||
private Object tag;
|
||||
|
||||
|
||||
/**
|
||||
* 构建一个
|
||||
*/
|
||||
public CodeModel() {
|
||||
|
||||
}
|
||||
/**
|
||||
* 构建一个
|
||||
* @param code 授权码
|
||||
* @param clientId 应用id
|
||||
* @param scope 请求授权范围
|
||||
* @param loginId 对应的账号id
|
||||
*/
|
||||
public CodeModel(String code, String clientId, String scope, Object loginId) {
|
||||
super();
|
||||
this.code = code;
|
||||
this.clientId = clientId;
|
||||
this.scope = scope;
|
||||
this.loginId = loginId;
|
||||
this.isConfirm = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return code
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param code 要设置的 code
|
||||
*/
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return clientId
|
||||
*/
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clientId 要设置的 clientId
|
||||
*/
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return scope
|
||||
*/
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scope 要设置的 scope
|
||||
*/
|
||||
public void setScope(String scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return loginId
|
||||
*/
|
||||
public Object getLoginId() {
|
||||
return loginId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loginId 要设置的 loginId
|
||||
*/
|
||||
public void setLoginId(Object loginId) {
|
||||
this.loginId = loginId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return isConfirm
|
||||
*/
|
||||
public Boolean getIsConfirm() {
|
||||
return isConfirm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isConfirm 要设置的 isConfirm
|
||||
*/
|
||||
public void setIsConfirm(Boolean isConfirm) {
|
||||
this.isConfirm = isConfirm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return redirectUri
|
||||
*/
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param redirectUri 要设置的 redirectUri
|
||||
*/
|
||||
public void setRedirectUri(String redirectUri) {
|
||||
this.redirectUri = redirectUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return rejectUri
|
||||
*/
|
||||
public String getRejectUri() {
|
||||
return rejectUri;
|
||||
}
|
||||
/**
|
||||
* @param rejectUri 要设置的 rejectUri
|
||||
*/
|
||||
public void setRejectUri(String rejectUri) {
|
||||
this.rejectUri = rejectUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return tag
|
||||
*/
|
||||
public Object getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tag 要设置的 tag
|
||||
*/
|
||||
public void setTag(Object tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
package cn.dev33.satoken.oauth2.model;
|
||||
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* 请求授权参数的Model
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class RequestAuthModel {
|
||||
|
||||
/**
|
||||
* 应用id
|
||||
*/
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* 授权范围
|
||||
*/
|
||||
private String scope;
|
||||
|
||||
/**
|
||||
* 对应的账号id
|
||||
*/
|
||||
private Object loginId;
|
||||
|
||||
/**
|
||||
* 待重定向URL
|
||||
*/
|
||||
private String redirectUri;
|
||||
|
||||
/**
|
||||
* 授权类型, 非必填
|
||||
*/
|
||||
private String responseType;
|
||||
|
||||
/**
|
||||
* 状态标识, 可为null
|
||||
*/
|
||||
private String state;
|
||||
|
||||
|
||||
/**
|
||||
* @return clientId
|
||||
*/
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clientId 要设置的 clientId
|
||||
*/
|
||||
public RequestAuthModel setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return scope
|
||||
*/
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scope 要设置的 scope
|
||||
*/
|
||||
public RequestAuthModel setScope(String scope) {
|
||||
this.scope = scope;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return loginId
|
||||
*/
|
||||
public Object getLoginId() {
|
||||
return loginId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loginId 要设置的 loginId
|
||||
*/
|
||||
public RequestAuthModel setLoginId(Object loginId) {
|
||||
this.loginId = loginId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return redirectUri
|
||||
*/
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param redirectUri 要设置的 redirectUri
|
||||
*/
|
||||
public RequestAuthModel setRedirectUri(String redirectUri) {
|
||||
this.redirectUri = redirectUri;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return responseType
|
||||
*/
|
||||
public String getResponseType() {
|
||||
return responseType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param responseType 要设置的 responseType
|
||||
*/
|
||||
public RequestAuthModel setResponseType(String responseType) {
|
||||
this.responseType = responseType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return state
|
||||
*/
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param state 要设置的 state
|
||||
*/
|
||||
public RequestAuthModel setState(String state) {
|
||||
this.state = state;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查此Model参数是否有效
|
||||
*/
|
||||
public RequestAuthModel checkModel() {
|
||||
if(SaTokenInsideUtil.isEmpty(clientId)) {
|
||||
throw new SaTokenException("无效client_id");
|
||||
}
|
||||
if(SaTokenInsideUtil.isEmpty(scope)) {
|
||||
throw new SaTokenException("无效scope");
|
||||
}
|
||||
if(SaTokenInsideUtil.isEmpty(redirectUri)) {
|
||||
throw new SaTokenException("无效redirect_uri");
|
||||
}
|
||||
if(SaTokenInsideUtil.isEmpty(String.valueOf(loginId))) {
|
||||
throw new SaTokenException("无效LoginId");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package cn.dev33.satoken.oauth2.model;
|
||||
|
||||
/**
|
||||
* 权限Model
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class ScopeModel {
|
||||
|
||||
/**
|
||||
* 权限名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 详细介绍
|
||||
*/
|
||||
private String introduce;
|
||||
|
||||
|
||||
/**
|
||||
* 构造一个
|
||||
*/
|
||||
public ScopeModel() {
|
||||
super();
|
||||
}
|
||||
/**
|
||||
* 构造一个
|
||||
* @param id 权限id
|
||||
* @param introduce 权限详细介绍
|
||||
*/
|
||||
public ScopeModel(String name, String introduce) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.introduce = introduce;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name 要设置的 name
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return introduce
|
||||
*/
|
||||
public String getIntroduce() {
|
||||
return introduce;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param introduce 要设置的 introduce
|
||||
*/
|
||||
public void setIntroduce(String introduce) {
|
||||
this.introduce = introduce;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package cn.dev33.satoken.oauth2.util;
|
||||
|
||||
/**
|
||||
* sa-token oauth2 模块 用到的所有常量
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaOAuth2Consts {
|
||||
|
||||
/**
|
||||
* 在保存授权码时用到的key
|
||||
*/
|
||||
public static final String UNLIMITED_DOMAIN = "*";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package cn.dev33.satoken.oauth2.util;
|
||||
|
||||
/**
|
||||
* sa-token-oauth2 模块内部算法util
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaOAuth2InsideUtil {
|
||||
|
||||
/**
|
||||
* 验证URL的正则表达式
|
||||
*/
|
||||
static final String URL_REGEX = "(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]";
|
||||
|
||||
/**
|
||||
* 使用正则表达式判断一个字符串是否为URL
|
||||
* @param str 字符串
|
||||
* @return 拼接后的url字符串
|
||||
*/
|
||||
public static boolean isUrl(String str) {
|
||||
if(str == null) {
|
||||
return false;
|
||||
}
|
||||
return str.toLowerCase().matches(URL_REGEX);
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user