@@ -32,9 +32,6 @@ public class FileDownloadController { | |||
@Autowired | |||
private FilePathGenerator filePathGenerator; | |||
@Autowired | |||
private RedisManager redisManager; | |||
/** | |||
* path方式下载文件 | |||
*/ | |||
@@ -56,8 +53,6 @@ public class FileDownloadController { | |||
@RequestParam(value = "noCache", required = false) Boolean noCache, | |||
HttpServletRequest request | |||
) throws FileNotFoundException { | |||
SessionUtil.checkSession(request, redisManager); | |||
File file = new File(filePathGenerator.getFileDiskPath(path)); | |||
if (!file.exists()) { | |||
@@ -30,9 +30,6 @@ public class FileUploadController { | |||
private DbEngine dbEngine; | |||
@Autowired | |||
private RedisManager redisManager; | |||
@Autowired | |||
private ImageAttachDao imageAttachDao; | |||
// TODO: 权限处理,临时文件处理 | |||
@@ -44,7 +41,6 @@ public class FileUploadController { | |||
@RequestParam(value = "keepName", required = false) Boolean keepName, | |||
HttpServletRequest request | |||
) { | |||
SessionUtil.checkSession(request, redisManager); | |||
return uploadFile(path, file, ThumbImage.type(thumb), thumbHeight, insert, keepName); | |||
} | |||
@@ -54,8 +50,6 @@ public class FileUploadController { | |||
@RequestParam(value = "thumbHeight", required = false) Integer thumbHeight, | |||
@RequestParam(value = "commit", required = false) Boolean insert, | |||
HttpServletRequest request) { | |||
SessionUtil.checkSession(request, redisManager); | |||
MultipartFile file = MemMultipartFile.build(data.getData()); | |||
if (file == null) { | |||
return R.error("数据内容格式有错"); | |||
@@ -70,7 +64,6 @@ public class FileUploadController { | |||
@RequestParam(value = "commit", required = false) Boolean insert, | |||
@RequestParam(value = "keepName", required = false) Boolean keepName, | |||
HttpServletRequest request) { | |||
SessionUtil.checkSession(request, redisManager); | |||
return uploadFile(path, file, ThumbImage.TYPE_AVATAR, size, insert, keepName); | |||
} | |||
@@ -81,7 +74,6 @@ public class FileUploadController { | |||
@RequestParam(value = "thumbHeight", required = false) Integer thumbHeight, | |||
@RequestParam(value = "keepName", required = false) Boolean keepName, | |||
HttpServletRequest request) { | |||
SessionUtil.checkSession(request, redisManager); | |||
return uploadFile(path, file, ThumbImage.type(thumb), thumbHeight, true, keepName); | |||
} | |||
@@ -143,8 +135,6 @@ public class FileUploadController { | |||
// TODO: 修改为安全的后台删除方式 | |||
@PostMapping("/fs/remove") | |||
public R remove(@RequestParam(value = "filePath") String filePath, HttpServletRequest request) { | |||
SessionUtil.checkSession(request, redisManager); | |||
File file = new File(filePathGenerator.getFileDiskPath(filePath)); | |||
if (file.exists() && file.isFile()) { | |||
if (file.delete()) { | |||
@@ -2,7 +2,6 @@ package cc.smtweb.system.bpm.web.login; | |||
import cc.smtweb.framework.core.annotation.SwBody; | |||
import cc.smtweb.framework.core.annotation.SwParam; | |||
import cc.smtweb.framework.core.annotation.SwPerm; | |||
import cc.smtweb.framework.core.annotation.SwService; | |||
import cc.smtweb.framework.core.common.R; | |||
import cc.smtweb.framework.core.common.SwMap; | |||
@@ -28,7 +27,6 @@ public class AuthService { | |||
@SwParam | |||
private SessionManager sessionManager; | |||
@SwPerm() | |||
public R getParty(@SwParam("username") String username) { | |||
Set<Party> partySet = PartyCache.getInstance().getTopSet(); | |||
List<SwMap> list = new ArrayList<>(); | |||
@@ -43,7 +41,6 @@ public class AuthService { | |||
return R.success(list); | |||
} | |||
@SwPerm(SwPerm.NONE) | |||
public R login(@SwBody LoginVO loginPO) { | |||
SwMap data = new SwMap(); | |||
User user = null; | |||
@@ -96,12 +93,10 @@ public class AuthService { | |||
return R.success(data); | |||
} | |||
@SwPerm() | |||
public R ping(@SwParam("msg") String msg) { | |||
return R.success(msg); | |||
} | |||
@SwPerm("user:edit") | |||
public R config(@SwParam("username") String username) { | |||
return R.success("config: " + username); | |||
} | |||
@@ -1,7 +1,15 @@ | |||
package cc.smtweb.framework.core; | |||
import cc.smtweb.framework.core.cache.redis.RedisManager; | |||
import cc.smtweb.framework.core.common.SwConsts; | |||
import cc.smtweb.framework.core.exception.BizException; | |||
import cc.smtweb.framework.core.mvc.controller.IEditor; | |||
import cc.smtweb.framework.core.mvc.realm.exception.ForbiddenException; | |||
import cc.smtweb.framework.core.mvc.realm.exception.UnauthenticatedException; | |||
import cc.smtweb.framework.core.session.SessionUtil; | |||
import cc.smtweb.framework.core.session.UserSession; | |||
import cc.smtweb.framework.core.util.NumberUtil; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.springframework.web.servlet.HandlerInterceptor; | |||
import org.springframework.web.servlet.ModelAndView; | |||
@@ -15,6 +23,7 @@ public class CoreInterceptor implements HandlerInterceptor { | |||
@Override | |||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { | |||
if (!SwConsts.SysParam.SYS_STARTED) throw new BizException("系统启动中,请稍候..."); | |||
SessionUtil.checkSession(request); | |||
return HandlerInterceptor.super.preHandle(request, response, handler); | |||
} | |||
@@ -1,26 +0,0 @@ | |||
package cc.smtweb.framework.core.annotation; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
/** | |||
* 被该注释修饰的方法都会经过切面拦截校验权限,默认是需要已登录权限 | |||
* | |||
* @author kevin | |||
*/ | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Target({ElementType.METHOD, ElementType.TYPE}) | |||
public @interface SwPerm { | |||
/** | |||
* 无权限控制的值,在函数上注解@SwPerm(SwPerm.NONE) | |||
*/ | |||
static final String NONE = "*"; | |||
static final String SESSION = ""; | |||
/** | |||
* 权限定义值 | |||
*/ | |||
String value() default SESSION; | |||
} |
@@ -8,7 +8,7 @@ import java.util.concurrent.ConcurrentHashMap; | |||
*/ | |||
public class SessionCacheFactory { | |||
private static SessionCacheFactory INSTANCE = null; | |||
private Map<Long, SessionCache> buffer = new ConcurrentHashMap<>(); | |||
private Map<String, SessionCache> buffer = new ConcurrentHashMap<>(); | |||
private SessionCacheFactory() { | |||
} | |||
@@ -27,10 +27,10 @@ public class SessionCacheFactory { | |||
//得到用户缓存对象 | |||
public SessionCache getUserCache(long userId) { | |||
return getUserCache(userId, 0L); | |||
return getCache(String.valueOf(userId), 0L); | |||
} | |||
public SessionCache getUserCache(long userId, long timeout) { | |||
public SessionCache getCache(String userId, long timeout) { | |||
SessionCache cache; | |||
cache = buffer.get(userId); | |||
@@ -41,11 +41,12 @@ public class SessionCacheFactory { | |||
buffer.put(userId, cache); | |||
} | |||
return cache; | |||
} | |||
//删除用户缓存 | |||
public void remove(String userId) { | |||
buffer.remove(userId); | |||
} | |||
} |
@@ -13,8 +13,17 @@ public interface SwConsts { | |||
public static String RUN_PROJECTS = ""; | |||
} | |||
//错误码 | |||
interface ErrorCode { | |||
//未登录 | |||
int NO_LOGIN = 101; | |||
//指纹错误 | |||
int TOKEN_INVALID = 102; | |||
} | |||
//启动顺序默认值 | |||
int DEFAULT_ORDER = 1; | |||
//缓存中:树节点按parent的key | |||
String KEY_PARENT_ID = "pr"; | |||
//级次码、字符串连接符 | |||
@@ -10,6 +10,8 @@ public class BizException extends RuntimeException { | |||
* | |||
*/ | |||
private static final long serialVersionUID = 1L; | |||
//错误编号 | |||
private int code = 0; | |||
public BizException() { | |||
super(); | |||
@@ -23,7 +25,20 @@ public class BizException extends RuntimeException { | |||
super(message); | |||
} | |||
public BizException(int code) { | |||
super(); | |||
this.code = code; | |||
} | |||
public BizException(int code, String message) { | |||
super(message); | |||
this.code = code; | |||
} | |||
public BizException(Throwable cause) { | |||
super(cause.getMessage(), cause); | |||
} | |||
public int getCode() { | |||
return code; | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
package cc.smtweb.framework.core.exception; | |||
import cc.smtweb.framework.core.common.R; | |||
import org.springframework.web.bind.annotation.ControllerAdvice; | |||
import org.springframework.web.bind.annotation.ExceptionHandler; | |||
import org.springframework.web.bind.annotation.ResponseBody; | |||
/** | |||
* Created by Akmm at 2022-09-07 20:13 | |||
* 异常统一处理 | |||
*/ | |||
@ControllerAdvice | |||
public class SwExceptionHandler { | |||
/** | |||
* 系统异常处理 | |||
* | |||
* @param e | |||
* @return | |||
*/ | |||
@ExceptionHandler(Exception.class) | |||
@ResponseBody | |||
public R error(Exception e) { | |||
if (e instanceof BizException) { | |||
return R.error(((BizException) e).getCode(), e.getMessage()); | |||
} | |||
e.printStackTrace(); | |||
return R.error("系统异常,请联系技术支持人员!"); | |||
} | |||
} |
@@ -64,7 +64,7 @@ public class WebMvcConfig implements WebMvcConfigurer { | |||
@Bean | |||
public MethodAccessManager methodAccessManager(CacheManager cacheManager) { | |||
return new MethodAccessManager(redisManager, cacheManager); | |||
return new MethodAccessManager(cacheManager); | |||
} | |||
@Override | |||
@@ -8,7 +8,6 @@ import cc.smtweb.framework.core.mvc.SchedulerManager; | |||
import cc.smtweb.framework.core.mvc.controller.access.IMethodAccess; | |||
import cc.smtweb.framework.core.mvc.controller.access.MethodAccess; | |||
import cc.smtweb.framework.core.mvc.controller.scan.BeanManager; | |||
import cc.smtweb.framework.core.mvc.realm.interceptor.PermInterceptor; | |||
import cc.smtweb.framework.core.mvc.realm.service.PermChecker; | |||
import cc.smtweb.framework.core.mvc.scheduler.SchedulerTaskManager; | |||
import lombok.Getter; | |||
@@ -27,15 +26,13 @@ import java.util.Map; | |||
public class MethodAccessManager { | |||
private Map<String, IMethodAccess> controllers; | |||
private IBeanContext beanContext; | |||
private PermInterceptor permInterceptor; | |||
private SchedulerTaskManager schedulerTaskManager; | |||
private MethodAccess[] destroyMethods; | |||
@Getter | |||
private CacheManager cacheManager; | |||
public MethodAccessManager(RedisManager redisManager, CacheManager cacheManager) { | |||
permInterceptor = new PermInterceptor(redisManager); | |||
public MethodAccessManager(CacheManager cacheManager) { | |||
this.cacheManager = cacheManager; | |||
} | |||
@@ -47,8 +44,6 @@ public class MethodAccessManager { | |||
IMethodAccess methodAccess = controllers.get(url); | |||
if (methodAccess != null) { | |||
permInterceptor.preHandle(request, methodAccess.getPerm()); | |||
return methodAccess.invoke(beanContext, params, body, request); | |||
} | |||
@@ -71,7 +66,6 @@ public class MethodAccessManager { | |||
this.beanContext = beanManager.getBeanContext(); | |||
this.controllers = beanManager.getControllers(); | |||
this.destroyMethods = beanManager.loadDestroyMethods(); | |||
this.permInterceptor.setCache(cache); | |||
// 启动定时任务 | |||
this.schedulerTaskManager = SchedulerTaskManager.build(beanContext, beanManager.getTasks()); | |||
@@ -0,0 +1,34 @@ | |||
package cc.smtweb.framework.core.mvc.controller; | |||
import cc.smtweb.framework.core.common.R; | |||
import cc.smtweb.framework.core.session.SessionUtil; | |||
import org.springframework.core.MethodParameter; | |||
import org.springframework.http.MediaType; | |||
import org.springframework.http.server.ServerHttpRequest; | |||
import org.springframework.http.server.ServerHttpResponse; | |||
import org.springframework.web.bind.annotation.ControllerAdvice; | |||
import org.springframework.web.context.request.RequestContextHolder; | |||
import org.springframework.web.context.request.ServletRequestAttributes; | |||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; | |||
/** | |||
* Created by Akmm at 2022-09-08 16:03 | |||
* 统一添加指纹 | |||
*/ | |||
@ControllerAdvice | |||
public class SwResponseBodyAdvice implements ResponseBodyAdvice { | |||
@Override | |||
public boolean supports(MethodParameter returnType, Class converterType) { | |||
Class targetClass = returnType.getMethod().getDeclaringClass(); | |||
return true; | |||
} | |||
@Override | |||
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { | |||
if (body instanceof R) { | |||
SessionUtil.setFingerValue(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(), (R)body); | |||
} | |||
return body; | |||
} | |||
} |
@@ -46,10 +46,4 @@ public interface IMethodAccess { | |||
*/ | |||
String fullName(); | |||
/** | |||
* API权限 | |||
* | |||
* @return 权限串 | |||
*/ | |||
String getPerm(); | |||
} |
@@ -22,14 +22,12 @@ import java.util.Map; | |||
public class MethodAccess implements IMethodAccess { | |||
private final ControllerAccess controllerAccess; | |||
private final Method method; | |||
@Getter | |||
private final String perm; | |||
private final MethodParamAccess[] paramBinds; | |||
public MethodAccess(ControllerAccess controllerAccess, Method method, String perm, MethodParamAccess[] paramBinds) { | |||
public MethodAccess(ControllerAccess controllerAccess, Method method, MethodParamAccess[] paramBinds) { | |||
this.controllerAccess = controllerAccess; | |||
this.method = method; | |||
this.perm = perm; | |||
this.paramBinds = paramBinds; | |||
} | |||
@@ -35,20 +35,13 @@ public class MethodParser { | |||
public void parse(Class<?> clazz, ControllerAccess controllerAccess, boolean isApi) throws ParseException { | |||
// this.controllerAccess = controllerAccess; | |||
// 服务的默认权限 | |||
String classPerm = ""; | |||
SwPerm swPerm = clazz.getAnnotation(SwPerm.class); | |||
if (swPerm != null) { | |||
classPerm = swPerm.value(); | |||
} | |||
// 扫描方法注解 | |||
for (Method m : clazz.getMethods()) { | |||
int modifier = m.getModifiers(); | |||
// && (R.class.isAssignableFrom(m.getReturnType())) | |||
if (Modifier.isPublic(modifier) && !Modifier.isStatic(modifier) && !Object.class.equals(m.getDeclaringClass())) { | |||
MethodAccess methodAccess = parseMethod(controllerAccess, m, classPerm); | |||
MethodAccess methodAccess = parseMethod(controllerAccess, m); | |||
if (scanContext.dealMethod(m, methodAccess)) { | |||
if (isApi) { | |||
@@ -81,7 +74,7 @@ public class MethodParser { | |||
} | |||
} | |||
private MethodAccess parseMethod(ControllerAccess controllerAccess, Method method, String defaultPerm) { | |||
private MethodAccess parseMethod(ControllerAccess controllerAccess, Method method) { | |||
Class<?>[] paramTypes = method.getParameterTypes(); | |||
Annotation[][] paramAnnotations = method.getParameterAnnotations(); | |||
@@ -146,14 +139,7 @@ public class MethodParser { | |||
} | |||
} | |||
// 得到权限注解 | |||
String perm = defaultPerm; | |||
SwPerm swPerm = method.getAnnotation(SwPerm.class); | |||
if (swPerm != null) { | |||
perm = swPerm.value(); | |||
} | |||
return new MethodAccess(controllerAccess, method, perm, paramBinds); | |||
return new MethodAccess(controllerAccess, method, paramBinds); | |||
} | |||
} |
@@ -1,110 +0,0 @@ | |||
package cc.smtweb.framework.core.mvc.realm.interceptor; | |||
import cc.smtweb.framework.core.annotation.SwPerm; | |||
import cc.smtweb.framework.core.cache.ISwCache; | |||
import cc.smtweb.framework.core.cache.redis.RedisManager; | |||
import cc.smtweb.framework.core.mvc.controller.IEditor; | |||
import cc.smtweb.framework.core.mvc.realm.exception.ForbiddenException; | |||
import cc.smtweb.framework.core.mvc.realm.exception.UnauthenticatedException; | |||
import cc.smtweb.framework.core.mvc.realm.service.PermCheckItem; | |||
import cc.smtweb.framework.core.mvc.realm.service.PermChecker; | |||
import cc.smtweb.framework.core.session.SessionUtil; | |||
import cc.smtweb.framework.core.session.UserSession; | |||
import org.apache.commons.lang3.StringUtils; | |||
import javax.servlet.http.HttpServletRequest; | |||
public class AbstractPermInterceptor { | |||
private final RedisManager redisManager; | |||
private ISwCache<Long, PermChecker> cacheService; | |||
public AbstractPermInterceptor(RedisManager redisManager) { | |||
this.redisManager = redisManager; | |||
} | |||
public void setCache(ISwCache<Long, PermChecker> cacheService) { | |||
this.cacheService = cacheService; | |||
} | |||
protected boolean handle(HttpServletRequest request, String permissionValue) { | |||
// 如果注解为null, 说明不需要拦截, 直接放过 | |||
if (SwPerm.NONE.equals(permissionValue)) { | |||
return true; | |||
} | |||
// redis读取session,判断是否登录 | |||
String token = SessionUtil.readToken(request); | |||
if ("design".equals(token)) return true; | |||
if (StringUtils.isBlank(token)) { | |||
throw new UnauthenticatedException("not find Auth-Token in header"); | |||
} | |||
UserSession us = redisManager.get(token, UserSession.class); | |||
if (us == null) { | |||
throw new UnauthenticatedException("not find UserSession by token: " + token); | |||
} | |||
request.setAttribute(IEditor.USER_TOKEN, token); | |||
request.setAttribute(IEditor.USER_SESSION, us); | |||
// 如果标记了权限注解,则判断权限 | |||
if (checkPermission(permissionValue, us)) { | |||
// 更新Token redis TTL | |||
redisManager.expire(token, RedisManager.SESSION_EXPIRE_SEC); | |||
return true; | |||
} else { | |||
throw new ForbiddenException("user not permission: " + permissionValue); | |||
} | |||
} | |||
/** | |||
* 权限检查 | |||
*/ | |||
private boolean checkPermission(String permissionValue, UserSession us) { | |||
if (StringUtils.isBlank(permissionValue)) { | |||
return true; | |||
} | |||
// 从本地缓存或数据库中获取该用户的权限信息 | |||
PermChecker permissionSet = cacheService.get(us.getUserId()); | |||
// if (MapUtils.isEmpty(permissionSet)) { | |||
// throw new ForbiddenException("empty permission"); | |||
// } | |||
PermCheckItem permChecker = permissionSet.get(permissionValue); | |||
if (permChecker != null) { | |||
return true; | |||
} | |||
while (true) { | |||
permissionValue = getParentPermValue(permissionValue); | |||
if (permissionValue != null) { | |||
permChecker = permissionSet.get(permissionValue); | |||
if (permChecker != null && permChecker.isPerfixMath()) { | |||
return true; | |||
} | |||
} else { | |||
break; | |||
} | |||
} | |||
return false; | |||
} | |||
private static String getParentPermValue(String permissionValue) { | |||
if (permissionValue.length() > 0) { | |||
int pos = permissionValue.lastIndexOf(':'); | |||
if (pos > 0) { | |||
return permissionValue.substring(0, pos); | |||
} | |||
return ""; | |||
} | |||
return null; | |||
} | |||
} |
@@ -1,42 +0,0 @@ | |||
package cc.smtweb.framework.core.mvc.realm.interceptor; | |||
import cc.smtweb.framework.core.annotation.SwPerm; | |||
import cc.smtweb.framework.core.cache.redis.RedisManager; | |||
import org.springframework.web.method.HandlerMethod; | |||
import org.springframework.web.servlet.HandlerInterceptor; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
/** | |||
* 暂时未使用,spring的拦截器方式判断权限 | |||
*/ | |||
public class AuthorizationInterceptor extends AbstractPermInterceptor implements HandlerInterceptor { | |||
public AuthorizationInterceptor(RedisManager redisManager) { | |||
super(redisManager); | |||
} | |||
@Override | |||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) | |||
throws Exception { | |||
if (handler instanceof HandlerMethod) { | |||
HandlerMethod handlerMethod = (HandlerMethod) handler; | |||
// 获取方法上的注解 | |||
SwPerm requiredSwPerm = handlerMethod.getMethod().getAnnotation(SwPerm.class); | |||
// 如果方法上的注解为空 则获取类的注解 | |||
if (requiredSwPerm == null) { | |||
requiredSwPerm = handlerMethod.getMethod().getDeclaringClass().getAnnotation(SwPerm.class); | |||
} | |||
String requiredValue = null; | |||
if (requiredSwPerm != null) { | |||
requiredValue = requiredSwPerm.value(); | |||
} | |||
return super.handle(request, requiredValue); | |||
} | |||
return true; | |||
} | |||
} |
@@ -1,27 +0,0 @@ | |||
package cc.smtweb.framework.core.mvc.realm.interceptor; | |||
import cc.smtweb.framework.core.cache.redis.RedisManager; | |||
import javax.servlet.http.HttpServletRequest; | |||
/** | |||
* 权限拦截器,在API请求处理中一起完成 | |||
* | |||
* @author xkliu | |||
*/ | |||
public class PermInterceptor extends AbstractPermInterceptor { | |||
public PermInterceptor(RedisManager redisManager) { | |||
super(redisManager); | |||
} | |||
/** | |||
* 校验用户是否有API权限 | |||
* | |||
* @param request http请求 | |||
* @param permissionValue 权限值 | |||
* @return 是否有权限 | |||
*/ | |||
public boolean preHandle(HttpServletRequest request, String permissionValue) { | |||
return super.handle(request, permissionValue); | |||
} | |||
} |
@@ -1,12 +1,22 @@ | |||
package cc.smtweb.framework.core.session; | |||
import cc.smtweb.framework.core.cache.SessionCache; | |||
import cc.smtweb.framework.core.cache.SessionCacheFactory; | |||
import cc.smtweb.framework.core.cache.redis.RedisManager; | |||
import cc.smtweb.framework.core.common.R; | |||
import cc.smtweb.framework.core.common.SwConsts; | |||
import cc.smtweb.framework.core.exception.BizException; | |||
import cc.smtweb.framework.core.mvc.controller.IEditor; | |||
import cc.smtweb.framework.core.mvc.realm.exception.UnauthenticatedException; | |||
import cc.smtweb.framework.core.util.NumberUtil; | |||
import cc.smtweb.framework.core.util.StringUtil; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.springframework.web.context.request.RequestContextHolder; | |||
import javax.servlet.http.HttpServletRequest; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.concurrent.atomic.AtomicInteger; | |||
/** | |||
* 〈session工具类〉 | |||
@@ -15,6 +25,94 @@ import javax.servlet.http.HttpServletRequest; | |||
* @since 1.0.0 | |||
*/ | |||
public class SessionUtil { | |||
//登录令牌header名 | |||
private final static String KEY_HEADER_SESSION = "Auth-Token"; | |||
//url参数名 | |||
private final static String KEY_PARAM_SESSION = "auth_token"; | |||
//防止重复提交指纹-header名 | |||
private final static String KEY_HEADER_FP_KEY = "Fpk_Token"; | |||
private final static String KEY_HEADER_FP_VAL = "Fpv_Token"; | |||
//url参数名 | |||
private final static String KEY_PARAM_FP_KEY = "fpk"; | |||
private final static String KEY_PARAM_FP_VAL = "fpv"; | |||
//不需要校验登录的url | |||
public static List<String> notLoginUrl = new ArrayList<>(); | |||
/*//不需要切换数据源的url,强制用主库 | |||
public static List<String> notSetDbUrl = new ArrayList<>();*/ | |||
//不需要验证合法性的url | |||
public static List<String> notCheckUrlValid = new ArrayList<>(); | |||
//不算用户交互操作的url,用于管理session失效 | |||
public static List<String> notInterActiveUrl = new ArrayList<>(); | |||
static { | |||
notLoginUrl.add("/db/*.do"); | |||
notLoginUrl.add("/custdb/*.do"); | |||
notLoginUrl.add("/attach/upload.do"); | |||
notLoginUrl.add("/attach/download.do"); | |||
notLoginUrl.add("/attach/uploadBase64.do"); | |||
notCheckUrlValid.add("/db/*.do"); | |||
notCheckUrlValid.add("/custdb/*.do"); | |||
notCheckUrlValid.add("/cust/common/httpService/service.do"); | |||
notCheckUrlValid.add("/attach/upload.do"); | |||
notCheckUrlValid.add("/attach/uploadBase64.do"); | |||
} | |||
private static void addUrl(String url, List<String> list) { | |||
list.add(url); | |||
} | |||
//是否不需要url校验 | |||
private static boolean isMatchUrl(String lasturi, List<String> list) { | |||
if (list.contains(lasturi)) return true; | |||
for (String s: list){ | |||
if (StringUtil.match(s, lasturi)) return true; | |||
} | |||
return false; | |||
} | |||
//是否不需要登录验证 | |||
public static boolean isNoLogin(String lasturi) { | |||
return isMatchUrl(lasturi, notLoginUrl); | |||
} | |||
//增加不需要登录的Uri | |||
public static void addNoLoginUri(String uri) { | |||
addUrl(uri, notLoginUrl); | |||
} | |||
/*//是否不需要切换库 | |||
public static boolean isNoSetDb(String lasturi) { | |||
return isMatchUrl(lasturi, notSetDbUrl); | |||
} | |||
//增加不需要登录的Uri | |||
public static void addNoSetDbUrl(String uri) { | |||
addUrl(uri, notSetDbUrl); | |||
}*/ | |||
//是否不需要url校验 | |||
public static boolean isNoCheckValid(String lasturi) { | |||
return isMatchUrl(lasturi, notCheckUrlValid); | |||
} | |||
//增加不需要校验合法性的Uri | |||
public static void addNoCheckValidUri(String uri) { | |||
addUrl(uri, notCheckUrlValid); | |||
} | |||
//不算用户交互操作 | |||
public static boolean isNoInterActive(String lasturi) { | |||
return isMatchUrl(lasturi, notInterActiveUrl); | |||
} | |||
//不算用户交互操作 | |||
public static void addNoInterActiveUri(String uri) { | |||
addUrl(uri, notInterActiveUrl); | |||
} | |||
private SessionUtil() { | |||
} | |||
@@ -23,41 +121,102 @@ public class SessionUtil { | |||
} | |||
public static String readToken(HttpServletRequest request) { | |||
String token = request.getHeader("Auth-Token"); | |||
return readToken(request, KEY_HEADER_SESSION, KEY_PARAM_SESSION); | |||
} | |||
public static String readFingerKey(HttpServletRequest request) { | |||
return readToken(request, KEY_HEADER_FP_KEY, KEY_PARAM_FP_KEY); | |||
} | |||
public static int readFingerVal(HttpServletRequest request) { | |||
return NumberUtil.getIntIgnoreErr(readToken(request, KEY_HEADER_FP_VAL, KEY_PARAM_FP_VAL)); | |||
} | |||
private static String readToken(HttpServletRequest request, String headerName, String paramName) { | |||
String token = request.getHeader(headerName); | |||
if (token == null) { | |||
token = request.getParameter("auth_token"); | |||
token = request.getParameter(paramName); | |||
} | |||
return token; | |||
} | |||
public static UserSession checkSession(HttpServletRequest request, RedisManager redisManager) { | |||
String token = readToken(request); | |||
//无效登录异常 | |||
public static void throwNoLogin() { | |||
throw new BizException(SwConsts.ErrorCode.NO_LOGIN); | |||
} | |||
if (StringUtils.isBlank(token)) { | |||
throw new UnauthenticatedException("not find Auth-Token in header"); | |||
} | |||
//校验是否登录 | |||
private static void checkLogin(HttpServletRequest request) { | |||
UserSession us = redisManager.get(token, UserSession.class); | |||
if (us == null) { | |||
throw new UnauthenticatedException("not find UserSession by token: " + token); | |||
} | |||
} | |||
return us; | |||
private static AtomicInteger getFingerVal(String id, String tokenkey) {//取并+1 | |||
SessionCache cache = SessionCacheFactory.getInstance().getCache(KEY_PARAM_FP_KEY, 1200L); | |||
return cache.get(id + "_" + tokenkey); | |||
} | |||
public static void checkSession(HttpServletRequest request) { | |||
//校验登录 | |||
String lasturi = getUriLast(request.getContextPath(), request.getServletPath()); | |||
String token = readToken(request); | |||
UserSession us = null; | |||
if (StringUtils.isNotBlank(token)) { | |||
us = RedisManager.getInstance().get(token, UserSession.class); | |||
} | |||
//校验登录 | |||
final boolean isNologin ="design".equals(token) || isNoLogin(lasturi); | |||
if (!isNologin) { | |||
if (us == null) { | |||
throwNoLogin(); | |||
} | |||
public static UserSession checkSession(String accessToken, RedisManager redisManager) { | |||
if (StringUtils.isBlank(accessToken)) { | |||
throw new UnauthenticatedException("not find Auth-Token in header"); | |||
request.setAttribute(IEditor.USER_TOKEN, token); | |||
request.setAttribute(IEditor.USER_SESSION, us); | |||
} | |||
UserSession us = redisManager.get(accessToken, UserSession.class); | |||
if (us == null) { | |||
throw new UnauthenticatedException("not find UserSession by token: " + accessToken); | |||
String fpk = readFingerKey(request); | |||
if (!StringUtil.isEmpty(fpk)) { | |||
//校验指纹 | |||
String fpKey = us != null ? String.valueOf(us.getUserId()): request.getSession(true).getId(); | |||
int fpv = readFingerVal(request); | |||
SessionCache cache = SessionCacheFactory.getInstance().getCache(KEY_PARAM_FP_KEY, 1200L); | |||
AtomicInteger ai = cache.get(fpKey + "_" + fpk); | |||
int bv = ai != null ? ai.get() : 0; | |||
if (fpv != bv) { | |||
throw new BizException(SwConsts.ErrorCode.TOKEN_INVALID, "指纹错误,请勿重复提交!"); | |||
} | |||
} | |||
} | |||
//设置指纹 | |||
public static void setFingerValue(HttpServletRequest request, R r) { | |||
String fpk = readFingerKey(request); | |||
if (!StringUtil.isEmpty(fpk)) { | |||
UserSession us = (UserSession) request.getAttribute(IEditor.USER_SESSION); | |||
String fpKey = us != null ? String.valueOf(us.getUserId()): request.getSession(true).getId(); | |||
SessionCache cache = SessionCacheFactory.getInstance().getCache(KEY_PARAM_FP_KEY, 1200L); | |||
AtomicInteger ai = cache.get(fpKey + "_" + fpk); | |||
if (ai == null) { | |||
cache.put(fpKey + "_" + fpk, new AtomicInteger(0)); | |||
} | |||
r.put(KEY_PARAM_FP_VAL, ai.getAndIncrement()); | |||
} | |||
} | |||
return us; | |||
/** | |||
* 截取请求名称,如重 /${contextPath}/framework/loginpage.do中截出loginpage.do; | |||
* | |||
* @param contextPath /dfp | |||
* @param uri /dfp/framework/loginpage.do | |||
*/ | |||
private static String getUriLast(String contextPath, String uri) { | |||
int index = uri.indexOf(contextPath+"/"); | |||
if (index < 0) return uri; | |||
if (index > 1) return uri; | |||
return uri.substring(index + contextPath.length()); | |||
} | |||
// private static Session getShiroSession() { | |||
@@ -27,5 +27,10 @@ | |||
<modules> | |||
<module>core</module> | |||
<module>bpm</module> | |||
<!--<module>../biz/qhcs-parent/qhcs</module> | |||
<module>../biz/pgzx-parent/wx-api</module> | |||
<module>../biz/pgzx-parent/pgzx</module>--> | |||
</modules> | |||
</project> |