├── CNAME ├── src ├── main │ └── java │ │ └── cn │ │ └── zifangsky │ │ └── easylimit │ │ ├── permission │ │ ├── annotation │ │ │ ├── Logical.java │ │ │ ├── RequiresLogin.java │ │ │ ├── RequiresRoles.java │ │ │ └── RequiresPermissions.java │ │ ├── PermissionInfo.java │ │ ├── aop │ │ │ ├── LoginAnnotationResolver.java │ │ │ ├── AbstractAnnotationMethodInterceptor.java │ │ │ ├── RolesAnnotationResolver.java │ │ │ ├── PermissionsAnnotationResolver.java │ │ │ ├── DefaultAnnotationMethodInterceptor.java │ │ │ ├── AbstractAnnotationResolver.java │ │ │ └── PermissionsAnnotationAdvisor.java │ │ └── impl │ │ │ └── SimplePermissionInfo.java │ │ ├── session │ │ ├── SessionKey.java │ │ ├── SessionIdFactory.java │ │ ├── SessionValidationScheduled.java │ │ ├── impl │ │ │ ├── support │ │ │ │ ├── UuidSessionIdFactory.java │ │ │ │ ├── RandomCharacterSessionIdFactory.java │ │ │ │ ├── SnowFlakeSessionIdFactory.java │ │ │ │ ├── SimpleAccessRefreshToken.java │ │ │ │ ├── DefaultCacheSessionDAO.java │ │ │ │ ├── DefaultCacheTokenDAO.java │ │ │ │ ├── CookieInfo.java │ │ │ │ ├── TokenInfo.java │ │ │ │ └── SimpleAccessToken.java │ │ │ ├── DefaultSessionFactory.java │ │ │ ├── DefaultSessionKey.java │ │ │ ├── TokenSessionContext.java │ │ │ ├── MemorySessionDAO.java │ │ │ ├── DefaultSessionContext.java │ │ │ ├── DefaultTokenOperateResolver.java │ │ │ ├── DefaultSessionValidationScheduled.java │ │ │ ├── ExposedSession.java │ │ │ └── MemoryTokenDAO.java │ │ ├── SessionFactory.java │ │ ├── SessionListener.java │ │ ├── SessionDAO.java │ │ ├── TokenOperateResolver.java │ │ ├── SessionContext.java │ │ └── TokenDAO.java │ │ ├── utils │ │ ├── PatternMatcher.java │ │ ├── AntPathMatcher.java │ │ ├── SecurityUtils.java │ │ ├── MapContext.java │ │ ├── SnowFlake.java │ │ └── BeanUtils.java │ │ ├── common │ │ ├── BindOperator.java │ │ ├── Constants.java │ │ └── SpringContextUtils.java │ │ ├── access │ │ ├── AccessFactory.java │ │ └── impl │ │ │ ├── DefaultAccessFactory.java │ │ │ ├── support │ │ │ ├── AccessRunnable.java │ │ │ ├── AccessCallable.java │ │ │ └── AccessBindOperator.java │ │ │ ├── TokenAccessFactory.java │ │ │ └── TokenAccessContext.java │ │ ├── filter │ │ ├── impl │ │ │ ├── AnonymousFilter.java │ │ │ ├── RolesVerifyFilter.java │ │ │ ├── PermissionsVerifyFilter.java │ │ │ ├── support │ │ │ │ ├── TokenRespMsg.java │ │ │ │ ├── ProxiedFilterChain.java │ │ │ │ ├── DefaultProxiedFilter.java │ │ │ │ ├── AbstractProxiedFilter.java │ │ │ │ ├── DefaultFilterEnums.java │ │ │ │ └── TokenProxiedFilter.java │ │ │ ├── DefaultLogoutFilter.java │ │ │ ├── PathFilterChainResolver.java │ │ │ └── DefaultLoginFilter.java │ │ ├── FilterChainResolver.java │ │ ├── AbstractVerifyFilter.java │ │ ├── AbstractOncePerRequestFilter.java │ │ ├── FilterChainManager.java │ │ ├── AbstractAccessControlFilter.java │ │ ├── AbstractPathFilter.java │ │ ├── AbstractFilter.java │ │ └── AbstractAdviceFilter.java │ │ ├── exception │ │ ├── EasyLimitException.java │ │ ├── authc │ │ │ ├── NoRoleException.java │ │ │ ├── NotLoginException.java │ │ │ ├── NoPermissionException.java │ │ │ ├── NoPrincipalInfoException.java │ │ │ ├── NoPermissionInfoException.java │ │ │ ├── IncorrectCredentialsException.java │ │ │ └── AuthenticationException.java │ │ ├── token │ │ │ ├── ExpiredTokenException.java │ │ │ ├── InvalidTokenException.java │ │ │ └── TokenException.java │ │ ├── cache │ │ │ └── CacheException.java │ │ ├── filter │ │ │ └── FilterException.java │ │ ├── access │ │ │ └── ExecutionException.java │ │ └── session │ │ │ ├── InvalidSessionException.java │ │ │ ├── ExpiredSessionException.java │ │ │ ├── UnknownSessionException.java │ │ │ ├── SessionException.java │ │ │ └── DisabledSessionException.java │ │ ├── authc │ │ ├── ValidatedInfo.java │ │ ├── PrincipalInfo.java │ │ └── impl │ │ │ ├── PhoneCodeValidatedInfo.java │ │ │ ├── SimplePrincipalInfo.java │ │ │ └── UsernamePasswordValidatedInfo.java │ │ ├── enums │ │ ├── ProjectModeEnums.java │ │ ├── DefaultTimeEnums.java │ │ ├── DefaultTokenRespEnums.java │ │ └── EncryptionTypeEnums.java │ │ ├── SecurityManager.java │ │ └── cache │ │ ├── Cache.java │ │ └── impl │ │ └── DefaultRedisCache.java └── test │ └── java │ └── cn │ └── zifangsky │ └── easylimit │ └── CommonTest.java ├── .gitignore └── README.md /CNAME: -------------------------------------------------------------------------------- 1 | easylimit.zifangsky.cn -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/permission/annotation/Logical.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.permission.annotation; 2 | 3 | /** 4 | * 逻辑运算 5 | * 6 | * @author zifangsky 7 | * @date 2019/6/18 8 | * @since 1.0.0 9 | */ 10 | public enum Logical { 11 | /** 12 | * 与 13 | */ 14 | AND, 15 | /** 16 | * 或 17 | */ 18 | OR 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | /target/ 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /nbbuild/ 22 | /dist/ 23 | /nbdist/ 24 | /.nb-gradle/ 25 | /build/ 26 | 27 | ### VS Code ### 28 | .vscode/ 29 | 30 | ### Maven ### 31 | .mvn 32 | mvnw* 33 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/SessionKey.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * {@link Session}的Key 7 | * 8 | * @author zifangsky 9 | * @date 2019/3/29 10 | * @since 1.0.0 11 | */ 12 | public interface SessionKey { 13 | /** 14 | * 获取SessionId 15 | * 16 | * @return java.io.Serializable 17 | * @author zifangsky 18 | * @date 2019/3/29 11:40 19 | * @since 1.0.0 20 | */ 21 | Serializable getSessionId(); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/utils/PatternMatcher.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.utils; 2 | 3 | /** 4 | * 使用正则表达式匹配路径 5 | * @author zifangsky 6 | * @date 2019/4/26 7 | * @since 1.0.0 8 | */ 9 | public interface PatternMatcher { 10 | 11 | /** 12 | * 判断给定的路径是否匹配特定路径模式 13 | * @author zifangsky 14 | * @date 2019/4/26 13:40 15 | * @since 1.0.0 16 | * @param pattern 特定路径模式 17 | * @param path 原路径 18 | * @return boolean 19 | */ 20 | boolean match(String pattern, String path); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/SessionIdFactory.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 用于创建sessionId 7 | * 8 | * @author zifangsky 9 | * @date 2019/3/29 10 | * @since 1.0.0 11 | */ 12 | public interface SessionIdFactory { 13 | 14 | /** 15 | * 生成sessionId 16 | * 17 | * @return java.io.Serializable 18 | * @author zifangsky 19 | * @date 2019/3/29 17:20 20 | * @since 1.0.0 21 | */ 22 | Serializable generateSessionId(); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/SessionValidationScheduled.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session; 2 | 3 | /** 4 | * 校验{@link Session}可用性的定时任务 5 | * 6 | * @author zifangsky 7 | * @date 2019/4/1 8 | * @since 1.0.0 9 | */ 10 | public interface SessionValidationScheduled { 11 | /** 12 | * 当前定时任务是否可用 13 | */ 14 | boolean isEnabled(); 15 | 16 | /** 17 | * 开始定时任务 18 | */ 19 | void startScheduled(); 20 | 21 | /** 22 | * 停止定时任务 23 | */ 24 | void stopScheduled(); 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/permission/annotation/RequiresLogin.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.permission.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 需要登录才能访问 10 | * @author zifangsky 11 | * @date 2019/6/18 12 | * @since 1.0.0 13 | */ 14 | @Target({ElementType.TYPE, ElementType.METHOD}) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface RequiresLogin { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/common/BindOperator.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.common; 2 | 3 | /** 4 | * 在某些逻辑执行之前做一些绑定操作 5 | * 6 | * @author zifangsky 7 | * @date 2019/4/4 8 | * @since 1.0.0 9 | */ 10 | public interface BindOperator { 11 | /** 12 | * 绑定操作 13 | * @author zifangsky 14 | * @date 2019/4/4 17:14 15 | * @since 1.0.0 16 | */ 17 | void bind(); 18 | 19 | /** 20 | * 恢复线程中的ThreadLocal数据状态 21 | * @author zifangsky 22 | * @date 2019/4/26 11:21 23 | * @since 1.0.0 24 | */ 25 | void recovery(); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/access/AccessFactory.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.access; 2 | 3 | /** 4 | * {@link Access}的创建工厂 5 | * 6 | * @author zifangsky 7 | * @date 2019/4/8 8 | * @since 1.0.0 9 | */ 10 | public interface AccessFactory { 11 | 12 | /** 13 | * 通过{@link AccessContext}创建{@link Access} 14 | * @author zifangsky 15 | * @date 2019/4/8 11:02 16 | * @since 1.0.0 17 | * @param accessContext accessContext 18 | * @return cn.zifangsky.easylimit.access.Access 19 | */ 20 | Access createAccess(AccessContext accessContext); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/support/UuidSessionIdFactory.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl.support; 2 | 3 | import cn.zifangsky.easylimit.session.SessionIdFactory; 4 | 5 | import java.io.Serializable; 6 | import java.util.UUID; 7 | 8 | /** 9 | * 通过{@link UUID}生成sessionId 10 | * 11 | * @author zifangsky 12 | * @date 2019/3/29 13 | * @since 1.0.0 14 | */ 15 | public class UuidSessionIdFactory implements SessionIdFactory { 16 | 17 | @Override 18 | public Serializable generateSessionId() { 19 | return UUID.randomUUID().toString(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/utils/AntPathMatcher.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.utils; 2 | 3 | public class AntPathMatcher implements PatternMatcher { 4 | 5 | /** 6 | * 这里的逻辑调用{@link org.springframework.util.AntPathMatcher}实现 7 | */ 8 | private org.springframework.util.PathMatcher springPathMatcher; 9 | 10 | public AntPathMatcher() { 11 | this.springPathMatcher = new org.springframework.util.AntPathMatcher(); 12 | } 13 | 14 | @Override 15 | public boolean match(String pattern, String path) { 16 | return springPathMatcher.match(pattern, path); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/SessionFactory.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session; 2 | 3 | /** 4 | * {@link Session}的创建工厂 5 | * 6 | * @author zifangsky 7 | * @date 2019/3/25 8 | * @since 1.0.0 9 | */ 10 | public interface SessionFactory { 11 | 12 | /** 13 | * 通过{@link SessionContext}创建{@link Session} 14 | * 15 | * @param sessionContext {@link Session}需要的基础数据 16 | * @return cn.zifangsky.easylimit.session.Session 17 | * @author zifangsky 18 | * @date 2019/3/26 15:11 19 | * @since 1.0.0 20 | */ 21 | Session createSession(SessionContext sessionContext); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/permission/annotation/RequiresRoles.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.permission.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 需要的角色 10 | * @author zifangsky 11 | * @date 2019/6/18 12 | * @since 1.0.0 13 | */ 14 | @Target({ElementType.TYPE, ElementType.METHOD}) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface RequiresRoles { 17 | /** 18 | * 角色值 19 | */ 20 | String[] value(); 21 | 22 | /** 23 | * 逻辑运算:并且、或者 24 | */ 25 | Logical logical() default Logical.AND; 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/permission/annotation/RequiresPermissions.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.permission.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 需要的权限 10 | * @author zifangsky 11 | * @date 2019/6/18 12 | * @since 1.0.0 13 | */ 14 | @Target({ElementType.TYPE, ElementType.METHOD}) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface RequiresPermissions { 17 | /** 18 | * 权限值 19 | */ 20 | String[] value(); 21 | 22 | /** 23 | * 逻辑运算:并且、或者 24 | */ 25 | Logical logical() default Logical.AND; 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/impl/AnonymousFilter.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter.impl; 2 | 3 | import cn.zifangsky.easylimit.filter.AbstractVerifyFilter; 4 | 5 | import javax.servlet.Filter; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | /** 10 | * 匿名访问的{@link Filter} 11 | * 12 | * @author zifangsky 13 | * @date 2019/5/8 14 | * @since 1.0.0 15 | */ 16 | public class AnonymousFilter extends AbstractVerifyFilter { 17 | 18 | @Override 19 | protected boolean isAccessAllowed(HttpServletRequest request, HttpServletResponse response, String[] controlVal) throws Exception { 20 | //始终返回true 21 | return true; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/permission/PermissionInfo.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.permission; 2 | 3 | import java.util.Set; 4 | 5 | /** 6 | * 角色+权限信息 7 | * 8 | * @author zifangsky 9 | * @date 2019/4/4 10 | * @since 1.0.0 11 | */ 12 | public interface PermissionInfo { 13 | /** 14 | * 获取所有角色信息 15 | * @author zifangsky 16 | * @date 2019/4/4 11:07 17 | * @since 1.0.0 18 | * @return java.util.Set 19 | */ 20 | Set getRoles(); 21 | 22 | /** 23 | * 获取所有权限信息 24 | * @author zifangsky 25 | * @date 2019/4/4 11:07 26 | * @since 1.0.0 27 | * @return java.util.Set 28 | */ 29 | Set getPermissions(); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/EasyLimitException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception; 2 | 3 | /** 4 | * easylimit项目的通用异常 5 | * 6 | * @author zifangsky 7 | * @date 2019/3/23 8 | * @since 1.0.0 9 | */ 10 | public class EasyLimitException extends RuntimeException { 11 | private static final long serialVersionUID = 7919839373220909439L; 12 | 13 | public EasyLimitException() { 14 | } 15 | 16 | public EasyLimitException(String message) { 17 | super(message); 18 | } 19 | 20 | public EasyLimitException(Throwable cause) { 21 | super(cause); 22 | } 23 | 24 | public EasyLimitException(String message, Throwable cause) { 25 | super(message, cause); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/authc/NoRoleException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.authc; 2 | 3 | /** 4 | * 没有角色异常 5 | * 6 | * @author zifangsky 7 | * @date 2019/6/19 8 | * @since 1.0.0 9 | */ 10 | public class NoRoleException extends AuthenticationException { 11 | private static final long serialVersionUID = 1636338538923061407L; 12 | 13 | public NoRoleException() { 14 | super(); 15 | } 16 | 17 | public NoRoleException(String message) { 18 | super(message); 19 | } 20 | 21 | public NoRoleException(Throwable cause) { 22 | super(cause); 23 | } 24 | 25 | public NoRoleException(String message, Throwable cause) { 26 | super(message, cause); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/authc/ValidatedInfo.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.authc; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 登录时来至外部的需要验证的信息 7 | * 8 | * @author zifangsky 9 | * @date 2019/4/3 10 | * @since 1.0.0 11 | */ 12 | public interface ValidatedInfo extends Serializable { 13 | /** 14 | * 获取主体,比如:用户名、手机号 15 | * 16 | * @return java.lang.String 17 | * @author zifangsky 18 | * @date 2019/4/3 18:18 19 | * @since 1.0.0 20 | */ 21 | String getSubject(); 22 | 23 | /** 24 | * 获取凭证信息,比如:密码、手机验证码 25 | * 26 | * @return java.lang.String 27 | * @author zifangsky 28 | * @date 2019/4/3 18:19 29 | * @since 1.0.0 30 | */ 31 | String getCredentials(); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/authc/NotLoginException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.authc; 2 | 3 | /** 4 | * 未登录异常 5 | * 6 | * @author zifangsky 7 | * @date 2019/6/19 8 | * @since 1.0.0 9 | */ 10 | public class NotLoginException extends AuthenticationException { 11 | private static final long serialVersionUID = 1194440512415730823L; 12 | 13 | public NotLoginException() { 14 | super(); 15 | } 16 | 17 | public NotLoginException(String message) { 18 | super(message); 19 | } 20 | 21 | public NotLoginException(Throwable cause) { 22 | super(cause); 23 | } 24 | 25 | public NotLoginException(String message, Throwable cause) { 26 | super(message, cause); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/token/ExpiredTokenException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.token; 2 | 3 | /** 4 | * 过期的Token 5 | * 6 | * @author zifangsky 7 | * @date 2019/3/25 8 | * @since 1.0.0 9 | */ 10 | public class ExpiredTokenException extends TokenException { 11 | private static final long serialVersionUID = 2737561889191511199L; 12 | 13 | public ExpiredTokenException() { 14 | super(); 15 | } 16 | 17 | public ExpiredTokenException(String message) { 18 | super(message); 19 | } 20 | 21 | public ExpiredTokenException(Throwable cause) { 22 | super(cause); 23 | } 24 | 25 | public ExpiredTokenException(String message, Throwable cause) { 26 | super(message, cause); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/token/InvalidTokenException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.token; 2 | 3 | /** 4 | * 不可用的Token 5 | * 6 | * @author zifangsky 7 | * @date 2019/3/25 8 | * @since 1.0.0 9 | */ 10 | public class InvalidTokenException extends TokenException { 11 | private static final long serialVersionUID = 6307461728213768148L; 12 | 13 | public InvalidTokenException() { 14 | super(); 15 | } 16 | 17 | public InvalidTokenException(String message) { 18 | super(message); 19 | } 20 | 21 | public InvalidTokenException(Throwable cause) { 22 | super(cause); 23 | } 24 | 25 | public InvalidTokenException(String message, Throwable cause) { 26 | super(message, cause); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/authc/NoPermissionException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.authc; 2 | 3 | /** 4 | * 没有权限异常 5 | * 6 | * @author zifangsky 7 | * @date 2019/6/19 8 | * @since 1.0.0 9 | */ 10 | public class NoPermissionException extends AuthenticationException { 11 | private static final long serialVersionUID = 318520983615710875L; 12 | 13 | public NoPermissionException() { 14 | super(); 15 | } 16 | 17 | public NoPermissionException(String message) { 18 | super(message); 19 | } 20 | 21 | public NoPermissionException(Throwable cause) { 22 | super(cause); 23 | } 24 | 25 | public NoPermissionException(String message, Throwable cause) { 26 | super(message, cause); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/cache/CacheException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.cache; 2 | 3 | import cn.zifangsky.easylimit.exception.EasyLimitException; 4 | 5 | /** 6 | * 缓存相关异常 7 | * 8 | * @author zifangsky 9 | * @date 2019/4/1 10 | * @since 1.0.0 11 | */ 12 | public class CacheException extends EasyLimitException { 13 | private static final long serialVersionUID = -8473442128035725315L; 14 | 15 | public CacheException() { 16 | super(); 17 | } 18 | 19 | public CacheException(String message) { 20 | super(message); 21 | } 22 | 23 | public CacheException(Throwable cause) { 24 | super(cause); 25 | } 26 | 27 | public CacheException(String message, Throwable cause) { 28 | super(message, cause); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/token/TokenException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.token; 2 | 3 | import cn.zifangsky.easylimit.exception.EasyLimitException; 4 | 5 | /** 6 | * Token相关异常 7 | * 8 | * @author zifangsky 9 | * @date 2019/3/23 10 | * @since 1.0.0 11 | */ 12 | public class TokenException extends EasyLimitException { 13 | private static final long serialVersionUID = 6565813579906927611L; 14 | 15 | public TokenException() { 16 | super(); 17 | } 18 | 19 | public TokenException(String message) { 20 | super(message); 21 | } 22 | 23 | public TokenException(Throwable cause) { 24 | super(cause); 25 | } 26 | 27 | public TokenException(String message, Throwable cause) { 28 | super(message, cause); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/authc/NoPrincipalInfoException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.authc; 2 | 3 | /** 4 | * 没有正确的用户主体信息异常 5 | * 6 | * @author zifangsky 7 | * @date 2019/4/11 8 | * @since 1.0.0 9 | */ 10 | public class NoPrincipalInfoException extends AuthenticationException { 11 | private static final long serialVersionUID = 8898016821504044651L; 12 | 13 | public NoPrincipalInfoException() { 14 | super(); 15 | } 16 | 17 | public NoPrincipalInfoException(String message) { 18 | super(message); 19 | } 20 | 21 | public NoPrincipalInfoException(Throwable cause) { 22 | super(cause); 23 | } 24 | 25 | public NoPrincipalInfoException(String message, Throwable cause) { 26 | super(message, cause); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/authc/NoPermissionInfoException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.authc; 2 | 3 | /** 4 | * 没有角色、权限信息异常 5 | * 6 | * @author zifangsky 7 | * @date 2019/4/11 8 | * @since 1.0.0 9 | */ 10 | public class NoPermissionInfoException extends AuthenticationException { 11 | private static final long serialVersionUID = -2146910736024645704L; 12 | 13 | public NoPermissionInfoException() { 14 | super(); 15 | } 16 | 17 | public NoPermissionInfoException(String message) { 18 | super(message); 19 | } 20 | 21 | public NoPermissionInfoException(Throwable cause) { 22 | super(cause); 23 | } 24 | 25 | public NoPermissionInfoException(String message, Throwable cause) { 26 | super(message, cause); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/filter/FilterException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.filter; 2 | 3 | import cn.zifangsky.easylimit.exception.EasyLimitException; 4 | 5 | /** 6 | * filter相关异常 7 | * 8 | * @author zifangsky 9 | * @date 2019/5/10 10 | * @since 1.0.0 11 | */ 12 | public class FilterException extends EasyLimitException { 13 | private static final long serialVersionUID = -3913958002519387906L; 14 | 15 | public FilterException() { 16 | super(); 17 | } 18 | 19 | public FilterException(String message) { 20 | super(message); 21 | } 22 | 23 | public FilterException(Throwable cause) { 24 | super(cause); 25 | } 26 | 27 | public FilterException(String message, Throwable cause) { 28 | super(message, cause); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/authc/IncorrectCredentialsException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.authc; 2 | 3 | /** 4 | * 密码错误异常 5 | * 6 | * @author zifangsky 7 | * @date 2019/4/11 8 | * @since 1.0.0 9 | */ 10 | public class IncorrectCredentialsException extends AuthenticationException { 11 | private static final long serialVersionUID = -3123740567200829829L; 12 | 13 | public IncorrectCredentialsException() { 14 | super(); 15 | } 16 | 17 | public IncorrectCredentialsException(String message) { 18 | super(message); 19 | } 20 | 21 | public IncorrectCredentialsException(Throwable cause) { 22 | super(cause); 23 | } 24 | 25 | public IncorrectCredentialsException(String message, Throwable cause) { 26 | super(message, cause); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/access/ExecutionException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.access; 2 | 3 | import cn.zifangsky.easylimit.exception.EasyLimitException; 4 | 5 | /** 6 | * 执行任务相关的异常 7 | * 8 | * @author zifangsky 9 | * @date 2019/4/8 10 | * @since 1.0.0 11 | */ 12 | public class ExecutionException extends EasyLimitException { 13 | private static final long serialVersionUID = 1322132876254013403L; 14 | 15 | public ExecutionException() { 16 | super(); 17 | } 18 | 19 | public ExecutionException(String message) { 20 | super(message); 21 | } 22 | 23 | public ExecutionException(Throwable cause) { 24 | super(cause); 25 | } 26 | 27 | public ExecutionException(String message, Throwable cause) { 28 | super(message, cause); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/session/InvalidSessionException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.session; 2 | 3 | import cn.zifangsky.easylimit.session.Session; 4 | 5 | /** 6 | * 不可用的{@link Session} 7 | * 8 | * @author zifangsky 9 | * @date 2019/3/25 10 | * @since 1.0.0 11 | */ 12 | public class InvalidSessionException extends SessionException { 13 | private static final long serialVersionUID = -2429699711337999603L; 14 | 15 | public InvalidSessionException() { 16 | super(); 17 | } 18 | 19 | public InvalidSessionException(String message) { 20 | super(message); 21 | } 22 | 23 | public InvalidSessionException(Throwable cause) { 24 | super(cause); 25 | } 26 | 27 | public InvalidSessionException(String message, Throwable cause) { 28 | super(message, cause); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/authc/AuthenticationException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.authc; 2 | 3 | import cn.zifangsky.easylimit.exception.EasyLimitException; 4 | 5 | /** 6 | * 登录认证相关的异常 7 | * 8 | * @author zifangsky 9 | * @date 2019/4/3 10 | * @since 1.0.0 11 | */ 12 | public class AuthenticationException extends EasyLimitException { 13 | private static final long serialVersionUID = 3878492919699820228L; 14 | 15 | public AuthenticationException() { 16 | super(); 17 | } 18 | 19 | public AuthenticationException(String message) { 20 | super(message); 21 | } 22 | 23 | public AuthenticationException(Throwable cause) { 24 | super(cause); 25 | } 26 | 27 | public AuthenticationException(String message, Throwable cause) { 28 | super(message, cause); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/session/ExpiredSessionException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.session; 2 | 3 | import cn.zifangsky.easylimit.session.Session; 4 | 5 | /** 6 | * 过期的{@link Session} 7 | * 8 | * @author zifangsky 9 | * @date 2019/3/25 10 | * @since 1.0.0 11 | */ 12 | public class ExpiredSessionException extends InvalidSessionException { 13 | private static final long serialVersionUID = 7124119918547850066L; 14 | 15 | public ExpiredSessionException() { 16 | super(); 17 | } 18 | 19 | public ExpiredSessionException(String message) { 20 | super(message); 21 | } 22 | 23 | public ExpiredSessionException(Throwable cause) { 24 | super(cause); 25 | } 26 | 27 | public ExpiredSessionException(String message, Throwable cause) { 28 | super(message, cause); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/session/UnknownSessionException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.session; 2 | 3 | import cn.zifangsky.easylimit.session.Session; 4 | 5 | /** 6 | * 未知的{@link Session} 7 | * 8 | * @author zifangsky 9 | * @date 2019/3/25 10 | * @since 1.0.0 11 | */ 12 | public class UnknownSessionException extends InvalidSessionException { 13 | private static final long serialVersionUID = -5971286828869438774L; 14 | 15 | public UnknownSessionException() { 16 | super(); 17 | } 18 | 19 | public UnknownSessionException(String message) { 20 | super(message); 21 | } 22 | 23 | public UnknownSessionException(Throwable cause) { 24 | super(cause); 25 | } 26 | 27 | public UnknownSessionException(String message, Throwable cause) { 28 | super(message, cause); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/FilterChainResolver.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter; 2 | 3 | import javax.servlet.FilterChain; 4 | import javax.servlet.http.HttpServletRequest; 5 | import javax.servlet.http.HttpServletResponse; 6 | 7 | /** 8 | * 用于获取代理{@link FilterChain} 9 | * 10 | * @author zifangsky 11 | * @date 2019/5/10 12 | * @since 1.0.0 13 | */ 14 | public interface FilterChainResolver { 15 | /** 16 | * 获取代理FilterChain 17 | * @author zifangsky 18 | * @date 2019/5/10 16:33 19 | * @since 1.0.0 20 | * @param request HttpServletRequest 21 | * @param response HttpServletResponse 22 | * @param originalChain 原FilterChain 23 | * @return javax.servlet.FilterChain 24 | */ 25 | FilterChain getProxiedFilterChain(HttpServletRequest request, HttpServletResponse response, FilterChain originalChain); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/SessionListener.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session; 2 | 3 | /** 4 | * {@link Session}监听器 5 | * 6 | * @author zifangsky 7 | * @date 2019/3/29 8 | * @since 1.0.0 9 | */ 10 | public interface SessionListener { 11 | /** 12 | * {@link Session}创建完成之后触发 13 | * 14 | * @author zifangsky 15 | * @date 2019/3/29 15:06 16 | * @since 1.0.0 17 | */ 18 | void onCreate(Session session); 19 | 20 | /** 21 | * {@link Session}停止之后触发 22 | * 23 | * @author zifangsky 24 | * @date 2019/3/29 15:06 25 | * @since 1.0.0 26 | */ 27 | void onStop(Session session); 28 | 29 | /** 30 | * {@link Session}过期之后触发 31 | * 32 | * @author zifangsky 33 | * @date 2019/3/29 15:06 34 | * @since 1.0.0 35 | */ 36 | void onExpiration(Session session); 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/session/SessionException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.session; 2 | 3 | import cn.zifangsky.easylimit.exception.EasyLimitException; 4 | import cn.zifangsky.easylimit.session.Session; 5 | 6 | /** 7 | * {@link Session}相关异常 8 | * 9 | * @author zifangsky 10 | * @date 2019/3/23 11 | * @since 1.0.0 12 | */ 13 | public class SessionException extends EasyLimitException { 14 | private static final long serialVersionUID = -7315660463328340994L; 15 | 16 | public SessionException() { 17 | super(); 18 | } 19 | 20 | public SessionException(String message) { 21 | super(message); 22 | } 23 | 24 | public SessionException(Throwable cause) { 25 | super(cause); 26 | } 27 | 28 | public SessionException(String message, Throwable cause) { 29 | super(message, cause); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/authc/PrincipalInfo.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.authc; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 用户主体 7 | * 8 | * @author zifangsky 9 | * @date 2019/4/3 10 | * @since 1.0.0 11 | */ 12 | public interface PrincipalInfo extends Serializable { 13 | 14 | /** 15 | * 获取账户名(必须唯一) 16 | * 17 | * @return java.lang.String 18 | * @author zifangsky 19 | * @date 2019/4/3 18:18 20 | * @since 1.0.0 21 | */ 22 | String getAccount(); 23 | 24 | /** 25 | * 获取用户主体 26 | * 27 | * @return T 28 | * @author zifangsky 29 | * @date 2019/4/3 17:58 30 | * @since 1.0.0 31 | */ 32 | Object getPrincipal(); 33 | 34 | /** 35 | * 获取密码 36 | * 37 | * @return java.lang.String 38 | * @author zifangsky 39 | * @date 2019/4/3 18:10 40 | * @since 1.0.0 41 | */ 42 | String getPassword(); 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/enums/ProjectModeEnums.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.enums; 2 | 3 | /** 4 | * 项目模式的枚举 5 | * 6 | * @author zifangsky 7 | * @date 2019/5/7 8 | * @since 1.0.0 9 | */ 10 | public enum ProjectModeEnums { 11 | /** 12 | * Token 13 | */ 14 | TOKEN("token"), 15 | /** 16 | * 默认 17 | */ 18 | DEFAULT("default"), 19 | ; 20 | 21 | /** 22 | * CODE 23 | */ 24 | private String code; 25 | 26 | ProjectModeEnums(String code) { 27 | this.code = code; 28 | } 29 | 30 | public String getCode() { 31 | return code; 32 | } 33 | 34 | public static ProjectModeEnums fromCode(String code){ 35 | if(code != null){ 36 | for(ProjectModeEnums e : values()){ 37 | if(e.getCode().equals(code)){ 38 | return e; 39 | } 40 | } 41 | } 42 | 43 | return null; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/permission/aop/LoginAnnotationResolver.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.permission.aop; 2 | 3 | import cn.zifangsky.easylimit.access.Access; 4 | import cn.zifangsky.easylimit.exception.authc.AuthenticationException; 5 | import cn.zifangsky.easylimit.permission.annotation.RequiresLogin; 6 | 7 | import java.lang.annotation.Annotation; 8 | 9 | /** 10 | * 登录认证相关AOP处理器 11 | * 12 | * @author zifangsky 13 | * @date 2019/6/19 14 | * @since 1.0.0 15 | */ 16 | public class LoginAnnotationResolver extends AbstractAnnotationResolver { 17 | 18 | public LoginAnnotationResolver() { 19 | super(RequiresLogin.class); 20 | } 21 | 22 | @Override 23 | public void assertPermission(Annotation annotation) throws AuthenticationException { 24 | if(!(annotation instanceof RequiresLogin)){ 25 | return; 26 | } 27 | 28 | Access access = this.getAccess(); 29 | access.checkPrincipal(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/impl/RolesVerifyFilter.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter.impl; 2 | 3 | import cn.zifangsky.easylimit.access.Access; 4 | import cn.zifangsky.easylimit.filter.AbstractVerifyFilter; 5 | 6 | import javax.servlet.Filter; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | /** 11 | * 角色相关校验的{@link Filter} 12 | * 13 | * @author zifangsky 14 | * @date 2019/5/8 15 | * @since 1.0.0 16 | */ 17 | public class RolesVerifyFilter extends AbstractVerifyFilter { 18 | 19 | @Override 20 | protected boolean isAccessAllowed(HttpServletRequest request, HttpServletResponse response, String[] rolesArray) throws Exception { 21 | if(rolesArray == null || rolesArray.length == 0){ 22 | return true; 23 | }else{ 24 | Access access = this.getAccess(request, response); 25 | return access.hasAllRoles(rolesArray); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/exception/session/DisabledSessionException.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.exception.session; 2 | 3 | import cn.zifangsky.easylimit.session.Session; 4 | 5 | /** 6 | *

不可用的{@link Session}

7 | * Note: 当应用已经禁用某个{@link Session},但是在其他地方又尝试去访问这个{@link Session}, 8 | * 就会抛出{@link DisabledSessionException}这个异常。 9 | * 10 | * @author zifangsky 11 | * @date 2019/3/25 12 | * @since 1.0.0 13 | */ 14 | public class DisabledSessionException extends SessionException { 15 | private static final long serialVersionUID = 1388734905398240705L; 16 | 17 | public DisabledSessionException() { 18 | super(); 19 | } 20 | 21 | public DisabledSessionException(String message) { 22 | super(message); 23 | } 24 | 25 | public DisabledSessionException(Throwable cause) { 26 | super(cause); 27 | } 28 | 29 | public DisabledSessionException(String message, Throwable cause) { 30 | super(message, cause); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/impl/PermissionsVerifyFilter.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter.impl; 2 | 3 | import cn.zifangsky.easylimit.access.Access; 4 | import cn.zifangsky.easylimit.filter.AbstractVerifyFilter; 5 | 6 | import javax.servlet.Filter; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | /** 11 | * 权限相关校验的{@link Filter} 12 | * 13 | * @author zifangsky 14 | * @date 2019/5/8 15 | * @since 1.0.0 16 | */ 17 | public class PermissionsVerifyFilter extends AbstractVerifyFilter { 18 | 19 | @Override 20 | protected boolean isAccessAllowed(HttpServletRequest request, HttpServletResponse response, String[] permissionsArray) throws Exception { 21 | if(permissionsArray == null || permissionsArray.length == 0){ 22 | return true; 23 | }else{ 24 | Access access = this.getAccess(request, response); 25 | return access.hasAllPermissions(permissionsArray); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/DefaultSessionFactory.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl; 2 | 3 | import cn.zifangsky.easylimit.session.Session; 4 | import cn.zifangsky.easylimit.session.SessionContext; 5 | import cn.zifangsky.easylimit.session.SessionFactory; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * 默认的{@link SessionFactory} 11 | * 12 | * @author zifangsky 13 | * @date 2019/3/26 14 | * @see SessionFactory 15 | * @since 1.0.0 16 | */ 17 | public class DefaultSessionFactory implements SessionFactory { 18 | 19 | @Override 20 | public Session createSession(SessionContext sessionContext) { 21 | if (sessionContext != null) { 22 | String host = sessionContext.getHost(); 23 | Serializable sessionId = sessionContext.getSessionId(); 24 | 25 | if (host != null && sessionId != null) { 26 | return new SimpleSession(sessionId, host); 27 | } 28 | } 29 | 30 | return new SimpleSession(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/permission/aop/AbstractAnnotationMethodInterceptor.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.permission.aop; 2 | 3 | import cn.zifangsky.easylimit.exception.authc.AuthenticationException; 4 | import org.aopalliance.intercept.MethodInterceptor; 5 | import org.aopalliance.intercept.MethodInvocation; 6 | 7 | /** 8 | * 处理权限注解的{@link MethodInterceptor} 9 | * 10 | * @author zifangsky 11 | * @date 2019/6/19 12 | * @since 1.0.0 13 | */ 14 | public abstract class AbstractAnnotationMethodInterceptor implements MethodInterceptor { 15 | /** 16 | * 校验角色、权限 17 | * @author zifangsky 18 | * @date 2019/6/19 18:33 19 | * @since 1.0.0 20 | * @param invocation MethodInvocation 21 | */ 22 | protected abstract void assertPermission(MethodInvocation invocation) throws AuthenticationException; 23 | 24 | @Override 25 | public Object invoke(MethodInvocation invocation) throws Throwable { 26 | this.assertPermission(invocation); 27 | return invocation.proceed(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/DefaultSessionKey.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl; 2 | 3 | import cn.zifangsky.easylimit.session.SessionKey; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 默认的{@link SessionKey} 9 | * 10 | * @author zifangsky 11 | * @date 2019/3/29 12 | * @since 1.0.0 13 | */ 14 | public class DefaultSessionKey implements SessionKey, Serializable { 15 | private static final long serialVersionUID = 5447080081332456113L; 16 | 17 | /** 18 | * SessionId 19 | */ 20 | private Serializable sessionId; 21 | 22 | public DefaultSessionKey() { 23 | } 24 | 25 | public DefaultSessionKey(Serializable sessionId) { 26 | this.sessionId = sessionId; 27 | } 28 | 29 | @Override 30 | public Serializable getSessionId() { 31 | return this.sessionId; 32 | } 33 | 34 | public void setSessionId(Serializable sessionId) { 35 | this.sessionId = sessionId; 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return "DefaultSessionKey{" + 41 | "sessionId=" + sessionId + 42 | '}'; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/support/RandomCharacterSessionIdFactory.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl.support; 2 | 3 | import cn.zifangsky.easylimit.session.SessionIdFactory; 4 | import cn.zifangsky.easylimit.utils.StringUtils; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * 通过随机字符串生成sessionId 10 | * 11 | * @author zifangsky 12 | * @date 2019/4/2 13 | * @since 1.0.0 14 | */ 15 | public class RandomCharacterSessionIdFactory implements SessionIdFactory { 16 | /** 17 | * 默认的生成的字符串长度 18 | */ 19 | private static final int DEFAULT_STRLEN = 30; 20 | /** 21 | * 生成的字符串长度 22 | */ 23 | private int strLen; 24 | 25 | public RandomCharacterSessionIdFactory() { 26 | this.strLen = DEFAULT_STRLEN; 27 | } 28 | 29 | public RandomCharacterSessionIdFactory(int strLen) { 30 | if (strLen < 1) { 31 | throw new IllegalArgumentException("strLen cannot be less than 1"); 32 | } 33 | 34 | this.strLen = strLen; 35 | } 36 | 37 | @Override 38 | public Serializable generateSessionId() { 39 | return StringUtils.getRandomStr(this.strLen); 40 | } 41 | 42 | public int getStrLen() { 43 | return strLen; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/support/SnowFlakeSessionIdFactory.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl.support; 2 | 3 | import cn.zifangsky.easylimit.session.SessionIdFactory; 4 | import cn.zifangsky.easylimit.utils.SnowFlake; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * 基于雪花算法生成sessionId 10 | * 11 | * @author zifangsky 12 | * @date 2019/4/2 13 | * @since 1.0.0 14 | */ 15 | public class SnowFlakeSessionIdFactory implements SessionIdFactory { 16 | /** 17 | * 数据中心 18 | */ 19 | private long datacenterId; 20 | /** 21 | * 机器标识 22 | */ 23 | private long machineId; 24 | /** 25 | * 雪花算法 26 | */ 27 | private SnowFlake snowFlake; 28 | 29 | public SnowFlakeSessionIdFactory(long datacenterId, long machineId) { 30 | this.datacenterId = datacenterId; 31 | this.machineId = machineId; 32 | this.snowFlake = new SnowFlake(datacenterId, machineId); 33 | } 34 | 35 | @Override 36 | public Serializable generateSessionId() { 37 | return String.valueOf(snowFlake.nextId()); 38 | } 39 | 40 | public long getDatacenterId() { 41 | return datacenterId; 42 | } 43 | 44 | 45 | public long getMachineId() { 46 | return machineId; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/utils/SecurityUtils.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.utils; 2 | 3 | import cn.zifangsky.easylimit.access.Access; 4 | import cn.zifangsky.easylimit.exception.EasyLimitException; 5 | import cn.zifangsky.easylimit.SecurityManager; 6 | 7 | /** 8 | * 用于静态获取{@link SecurityManager} 9 | * 10 | * @author zifangsky 11 | * @date 2019/4/4 12 | * @since 1.0.0 13 | */ 14 | public class SecurityUtils { 15 | /** 16 | * 静态保存一份{@link SecurityManager},用于备份 17 | */ 18 | private static SecurityManager securityManager; 19 | 20 | public static SecurityManager getSecurityManager() { 21 | //1. 获取ThreadLocal中的实例 22 | SecurityManager threadLocalRecord = ThreadContext.getSecurityManager(); 23 | 24 | if(threadLocalRecord == null){ 25 | //2. 如果为空,则获取备份的SecurityManager 26 | threadLocalRecord = SecurityUtils.securityManager; 27 | 28 | if(threadLocalRecord == null){ 29 | //3. 如果还为空,则抛出异常 30 | throw new EasyLimitException("There is no SecurityManager available"); 31 | } 32 | } 33 | 34 | return threadLocalRecord; 35 | } 36 | 37 | public static void setSecurityManager(SecurityManager securityManager) { 38 | SecurityUtils.securityManager = securityManager; 39 | } 40 | 41 | public static Access getAccess() { 42 | return ThreadContext.getAccess(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/access/impl/DefaultAccessFactory.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.access.impl; 2 | 3 | import cn.zifangsky.easylimit.access.Access; 4 | import cn.zifangsky.easylimit.access.AccessContext; 5 | import cn.zifangsky.easylimit.access.AccessFactory; 6 | import cn.zifangsky.easylimit.authc.PrincipalInfo; 7 | import cn.zifangsky.easylimit.SecurityManager; 8 | import cn.zifangsky.easylimit.session.Session; 9 | 10 | import javax.servlet.ServletRequest; 11 | import javax.servlet.ServletResponse; 12 | 13 | /** 14 | * 默认的{@link AccessFactory} 15 | * 16 | * @author zifangsky 17 | * @date 2019/4/8 18 | * @since 1.0.0 19 | */ 20 | public class DefaultAccessFactory implements AccessFactory { 21 | 22 | @Override 23 | public Access createAccess(AccessContext accessContext) { 24 | ServletRequest request = accessContext.acquireServletRequest(); 25 | ServletResponse response = accessContext.acquireServletResponse(); 26 | String host = accessContext.acquireHost(); 27 | Session session = accessContext.acquireSession(); 28 | boolean authenticated = accessContext.acquireAuthenticated(); 29 | PrincipalInfo principalInfo = accessContext.acquirePrincipalInfo(); 30 | SecurityManager securityManager = accessContext.acquireSecurityManager(); 31 | 32 | return new ExposedAccess(request, response, host, session, authenticated, principalInfo, securityManager); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/enums/DefaultTimeEnums.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.enums; 2 | 3 | import cn.zifangsky.easylimit.session.Session; 4 | 5 | import java.time.temporal.ChronoUnit; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | /** 9 | * 系统中默认时间的枚举 10 | * 11 | * @author zifangsky 12 | * @date 2019/3/26 13 | * @since 1.0.0 14 | */ 15 | public enum DefaultTimeEnums { 16 | /** 17 | * {@link Session}的默认超时时间(120分钟) 18 | */ 19 | SESSION_TIMEOUT(120, ChronoUnit.MINUTES, TimeUnit.MINUTES), 20 | /** 21 | * {@link Session}的默认校验频率(30分钟) 22 | */ 23 | SESSION_VALIDATION(30, ChronoUnit.MINUTES, TimeUnit.MINUTES), 24 | /** 25 | * Access Token的默认有效期为1天 26 | */ 27 | ACCESS_TOKEN(1, ChronoUnit.DAYS, TimeUnit.DAYS), 28 | /** 29 | * Refresh Token的默认有效期为180天 30 | */ 31 | REFRESH_TOKEN(180, ChronoUnit.DAYS, TimeUnit.DAYS) 32 | ; 33 | 34 | private long time; 35 | 36 | private ChronoUnit chronoUnit; 37 | 38 | private TimeUnit timeUnit; 39 | 40 | DefaultTimeEnums(long time, ChronoUnit chronoUnit, TimeUnit timeUnit) { 41 | this.time = time; 42 | this.chronoUnit = chronoUnit; 43 | this.timeUnit = timeUnit; 44 | } 45 | 46 | public long getTime() { 47 | return time; 48 | } 49 | 50 | public ChronoUnit getChronoUnit() { 51 | return chronoUnit; 52 | } 53 | 54 | public TimeUnit getTimeUnit() { 55 | return timeUnit; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/support/SimpleAccessRefreshToken.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl.support; 2 | 3 | /** 4 | * 包含Access Token和Refresh Token 5 | * 6 | * @author zifangsky 7 | * @date 2019/6/13 8 | * @since 1.0.0 9 | */ 10 | public class SimpleAccessRefreshToken { 11 | /** 12 | * Access Token 13 | */ 14 | private SimpleAccessToken accessToken; 15 | 16 | /** 17 | * Refresh Token 18 | */ 19 | private SimpleRefreshToken refreshToken; 20 | 21 | public SimpleAccessRefreshToken() { 22 | } 23 | 24 | public SimpleAccessRefreshToken(SimpleAccessToken accessToken, SimpleRefreshToken refreshToken) { 25 | this.accessToken = accessToken; 26 | this.refreshToken = refreshToken; 27 | } 28 | 29 | public SimpleAccessToken getAccessToken() { 30 | return accessToken; 31 | } 32 | 33 | public void setAccessToken(SimpleAccessToken accessToken) { 34 | this.accessToken = accessToken; 35 | } 36 | 37 | public SimpleRefreshToken getRefreshToken() { 38 | return refreshToken; 39 | } 40 | 41 | public void setRefreshToken(SimpleRefreshToken refreshToken) { 42 | this.refreshToken = refreshToken; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return "SimpleAccessRefreshToken{" + 48 | "accessToken=" + accessToken + 49 | ", refreshToken=" + refreshToken + 50 | '}'; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/permission/impl/SimplePermissionInfo.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.permission.impl; 2 | 3 | import cn.zifangsky.easylimit.permission.PermissionInfo; 4 | 5 | import java.util.Set; 6 | 7 | /** 8 | * {@link PermissionInfo}的基本实现 9 | * 10 | * @author zifangsky 11 | * @date 2019/4/4 12 | * @since 1.0.0 13 | */ 14 | public class SimplePermissionInfo implements PermissionInfo{ 15 | /** 16 | * 当前用户拥有的所有角色 17 | */ 18 | private Set roles; 19 | 20 | /** 21 | * 当前用户拥有的所有权限 22 | */ 23 | private Set permissions; 24 | 25 | public SimplePermissionInfo() { 26 | 27 | } 28 | 29 | public SimplePermissionInfo(Set roles, Set permissions) { 30 | this.roles = roles; 31 | this.permissions = permissions; 32 | } 33 | 34 | @Override 35 | public Set getRoles() { 36 | return this.roles; 37 | } 38 | 39 | @Override 40 | public Set getPermissions() { 41 | return this.permissions; 42 | } 43 | 44 | public void setRoles(Set roles) { 45 | this.roles = roles; 46 | } 47 | 48 | public void setPermissions(Set permissions) { 49 | this.permissions = permissions; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "SimplePermissionInfo{" + 55 | "roles=" + roles + 56 | ", permissions=" + permissions + 57 | '}'; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # easylimit # 2 | 3 | # 简介 # 4 | 5 | `easylimit`框架是一个同时适用于传统`MVC`项目开发模式以及`前后端分离`项目开发模式的权限控制框架。需要注意的是,目前此框架仅限于在基于`Spring`的`Java Web`项目中运行,且主要依赖`spring-context`、`Jackson`、`Jedis`这几个组件。 6 | 7 | ## 功能特性 ## 8 | 9 | 在使用上,目前主要提供了以下功能特性: 10 | 11 | - 同时支持`MVC`和`前后端分离`项目开发模式的权限控制 12 | - 支持完整的`RBAC`权限控制 13 | - 默认实现多种`session_id`生成方式,包括:`随机字符串`、`UUID`、`雪花算法` 14 | - 默认实现多种`session`和`token`存储方式,包括:基于`ConcurrentHashMap`的内存存储、使用`Redis`等缓存存储 15 | - 默认实现AOP切面,支持多种权限控制注解,包括:`@RequiresLogin`、`@RequiresPermissions`、`@RequiresRoles` 16 | - 默认支持多种`Access Token`传参方式,且可以灵活扩展 17 | - 默认实现“是否踢出当前用户的旧会话”的选项 18 | - 默认实现多种登录登录方式、多种密码校验规则的简单接入。前者包括:“用户名+密码”登录、“手机号码+短信验证码”登录,后者包括:`Base64`、`Md5Hex`、`Sha256Hex`、`Sha512Hex`、`Md5Crypt`、`Sha256Crypt`等其他自定义密码加密/摘要方式 19 | - 使用简单,可扩展性强 20 | - 代码规范,注释完整,文档齐全,有助于通过源码学习其实现思路 21 | 22 | ## 开始使用 ## 23 | 24 | 使用方式及详细说明可以查看这个文档站点:[https://easylimit.zifangsky.cn/](https://easylimit.zifangsky.cn/) 25 | 26 | 27 | 28 | ## 鸣谢 ## 29 | 30 | 前几年的时候,我很喜欢`Apache Shiro`这个权限控制框架,不过后面慢慢地`Shiro`不再满足项目开发的实际需求,从而让我萌生了自己动手开发一个权限控制框架的想法。不过在`easylimit`这个框架的早期开发阶段,我参考了很多`Shiro`的设计理念以及源码实现,在此我表示对`Apache Shiro`开发组及其社区由衷的感谢! 31 | 32 | - Apache Shiro官网:[https://shiro.apache.org/](https://shiro.apache.org/) 33 | - Apache Shiro源码:[https://github.com/apache/shiro](https://github.com/apache/shiro) 34 | 35 | 36 | 37 | 此外,我在设计基于`前后端分离`项目开发模式的权限控制时,也借鉴了一些`JSON Web Token (JWT) `的设计思想,在此也表示感谢! 38 | 39 | ## 最后 ## 40 | 41 | 欢迎大家积极使用这个框架,同时我也希望这个框架可以真正帮助到大家。如果大家在使用过程中有什么疑问或者好的建议,可以在项目中提 [Issue](https://github.com/zifangsky/easylimit/issues),也可以加这个Q群(**590424939**)跟我即时交流。 -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/permission/aop/RolesAnnotationResolver.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.permission.aop; 2 | 3 | import cn.zifangsky.easylimit.access.Access; 4 | import cn.zifangsky.easylimit.exception.authc.AuthenticationException; 5 | import cn.zifangsky.easylimit.permission.annotation.Logical; 6 | import cn.zifangsky.easylimit.permission.annotation.RequiresRoles; 7 | 8 | import java.lang.annotation.Annotation; 9 | 10 | /** 11 | * 角色相关AOP处理器 12 | * 13 | * @author zifangsky 14 | * @date 2019/6/19 15 | * @since 1.0.0 16 | */ 17 | public class RolesAnnotationResolver extends AbstractAnnotationResolver { 18 | 19 | public RolesAnnotationResolver() { 20 | super(RequiresRoles.class); 21 | } 22 | 23 | @Override 24 | public void assertPermission(Annotation annotation) throws AuthenticationException { 25 | if(!(annotation instanceof RequiresRoles)){ 26 | return; 27 | } 28 | 29 | RequiresRoles requiresRoles = (RequiresRoles)annotation; 30 | Access access = this.getAccess(); 31 | 32 | //1. 获取所有角色码 33 | String[] roles = requiresRoles.value(); 34 | 35 | if(roles.length == 1){ 36 | access.checkRole(roles[0]); 37 | return; 38 | } 39 | 40 | if(Logical.AND.equals(requiresRoles.logical())){ 41 | access.checkAllRoles(roles); 42 | return; 43 | } 44 | 45 | if(Logical.OR.equals(requiresRoles.logical())){ 46 | access.checkAnyRoles(roles); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/permission/aop/PermissionsAnnotationResolver.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.permission.aop; 2 | 3 | import cn.zifangsky.easylimit.access.Access; 4 | import cn.zifangsky.easylimit.exception.authc.AuthenticationException; 5 | import cn.zifangsky.easylimit.permission.annotation.Logical; 6 | import cn.zifangsky.easylimit.permission.annotation.RequiresPermissions; 7 | 8 | import java.lang.annotation.Annotation; 9 | 10 | /** 11 | * 权限相关AOP处理器 12 | * 13 | * @author zifangsky 14 | * @date 2019/6/19 15 | * @since 1.0.0 16 | */ 17 | public class PermissionsAnnotationResolver extends AbstractAnnotationResolver { 18 | 19 | public PermissionsAnnotationResolver() { 20 | super(RequiresPermissions.class); 21 | } 22 | 23 | @Override 24 | public void assertPermission(Annotation annotation) throws AuthenticationException { 25 | if(!(annotation instanceof RequiresPermissions)){ 26 | return; 27 | } 28 | 29 | RequiresPermissions requiresPermissions = (RequiresPermissions)annotation; 30 | Access access = this.getAccess(); 31 | 32 | //1. 获取所有权限码 33 | String[] perms = requiresPermissions.value(); 34 | 35 | if(perms.length == 1){ 36 | access.checkPermission(perms[0]); 37 | return; 38 | } 39 | 40 | if(Logical.AND.equals(requiresPermissions.logical())){ 41 | access.checkAllPermissions(perms); 42 | return; 43 | } 44 | 45 | if(Logical.OR.equals(requiresPermissions.logical())){ 46 | access.checkAnyPermissions(perms); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/SessionDAO.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session; 2 | 3 | import cn.zifangsky.easylimit.exception.session.UnknownSessionException; 4 | 5 | import java.io.Serializable; 6 | import java.util.Set; 7 | 8 | /** 9 | * {@link Session}的存储 10 | * 11 | * @author zifangsky 12 | * @date 2019/4/1 13 | * @since 1.0.0 14 | */ 15 | public interface SessionDAO { 16 | 17 | /** 18 | * 通过sessionId查询{@link Session} 19 | * 20 | * @param sessionId sessionId 21 | * @return cn.zifangsky.easylimit.session.Session 22 | * @throws UnknownSessionException UnknownSessionException 23 | * @author zifangsky 24 | * @date 2019/4/1 16:51 25 | * @since 1.0.0 26 | */ 27 | Session read(Serializable sessionId) throws UnknownSessionException; 28 | 29 | /** 30 | * 更新{@link Session} 31 | * 32 | * @param session session 33 | * @throws UnknownSessionException UnknownSessionException 34 | * @author zifangsky 35 | * @date 2019/4/1 16:52 36 | * @since 1.0.0 37 | */ 38 | void update(Session session) throws UnknownSessionException; 39 | 40 | /** 41 | * 删除{@link Session} 42 | * 43 | * @param session session 44 | * @author zifangsky 45 | * @date 2019/4/1 16:52 46 | * @since 1.0.0 47 | */ 48 | void delete(Session session); 49 | 50 | /** 51 | * 获取所有可用状态的{@link Session} 52 | * 53 | * @return java.util.Set 54 | * @author zifangsky 55 | * @date 2019/4/1 16:55 56 | * @since 1.0.0 57 | */ 58 | Set getActiveSessions(); 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/authc/impl/PhoneCodeValidatedInfo.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.authc.impl; 2 | 3 | import cn.zifangsky.easylimit.authc.ValidatedInfo; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | 6 | /** 7 | * 基于“手机号码+短信验证码”的登录验证信息 8 | * 9 | * @author zifangsky 10 | * @date 2019/4/3 11 | * @since 1.0.0 12 | */ 13 | public class PhoneCodeValidatedInfo implements ValidatedInfo { 14 | private static final long serialVersionUID = 5329929348382496608L; 15 | 16 | /** 17 | * 手机号码 18 | */ 19 | private String phone; 20 | 21 | /** 22 | * 短信验证码 23 | */ 24 | private String code; 25 | 26 | public PhoneCodeValidatedInfo() { 27 | } 28 | 29 | public PhoneCodeValidatedInfo(String phone, String code) { 30 | this.phone = phone; 31 | this.code = code; 32 | } 33 | 34 | @Override 35 | @JsonIgnore 36 | public String getSubject() { 37 | return this.phone; 38 | } 39 | 40 | @Override 41 | @JsonIgnore 42 | public String getCredentials() { 43 | return this.code; 44 | } 45 | 46 | public void setPhone(String phone) { 47 | this.phone = phone; 48 | } 49 | 50 | public void setCode(String code) { 51 | this.code = code; 52 | } 53 | 54 | public String getPhone() { 55 | return phone; 56 | } 57 | 58 | public String getCode() { 59 | return code; 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return "PhoneCodeValidatedInfo{" + 65 | "phone='" + phone + '\'' + 66 | ", code='" + code + '\'' + 67 | '}'; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/access/impl/support/AccessRunnable.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.access.impl.support; 2 | 3 | import cn.zifangsky.easylimit.access.Access; 4 | import cn.zifangsky.easylimit.common.BindOperator; 5 | 6 | /** 7 | * 在过滤链执行之前,做一些绑定操作 8 | * 9 | * @author zifangsky 10 | * @date 2019/4/4 11 | * @since 1.0.0 12 | */ 13 | public class AccessRunnable implements Runnable { 14 | /** 15 | * 绑定逻辑 16 | */ 17 | private BindOperator bindOperator; 18 | 19 | /** 20 | * 实际的线程执行逻辑 21 | */ 22 | private Runnable runnable; 23 | 24 | public AccessRunnable(Access access, Runnable runnable){ 25 | this(new AccessBindOperator(access), runnable); 26 | } 27 | 28 | protected AccessRunnable(BindOperator bindOperator, Runnable runnable) { 29 | if (bindOperator == null){ 30 | throw new IllegalArgumentException("Parameter bindOperator cannot be empty."); 31 | } 32 | if (runnable == null){ 33 | throw new IllegalArgumentException("Parameter runnable cannot be empty."); 34 | } 35 | 36 | this.bindOperator = bindOperator; 37 | this.runnable = runnable; 38 | } 39 | 40 | @Override 41 | public void run() { 42 | try { 43 | //1. 绑定 44 | this.bindOperator.bind(); 45 | //2. 继续执行线程的逻辑 46 | this.doRun(this.runnable); 47 | }finally { 48 | //3. 恢复状态 49 | this.bindOperator.recovery(); 50 | } 51 | } 52 | 53 | /** 54 | * 实际的线程执行逻辑 55 | * @author zifangsky 56 | * @date 2019/4/4 17:12 57 | * @since 1.0.0 58 | */ 59 | protected void doRun(Runnable runnable){ 60 | runnable.run(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/impl/support/TokenRespMsg.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter.impl.support; 2 | 3 | import cn.zifangsky.easylimit.enums.DefaultTokenRespEnums; 4 | 5 | /** 6 | * 基于Token的返回提示 7 | * 8 | * @author zifangsky 9 | * @date 2019/5/7 10 | * @since 1.0.0 11 | */ 12 | public class TokenRespMsg { 13 | /** 14 | * 返回状态码 15 | */ 16 | private Integer code; 17 | 18 | /** 19 | * 返回字段名 20 | */ 21 | private String name; 22 | 23 | /** 24 | * 返回提示信息 25 | */ 26 | private String msg; 27 | 28 | public TokenRespMsg() { 29 | } 30 | 31 | public TokenRespMsg(DefaultTokenRespEnums defaultTokenRespEnums) { 32 | this.code = defaultTokenRespEnums.getCode(); 33 | this.name = defaultTokenRespEnums.getName(); 34 | this.msg = defaultTokenRespEnums.getMsg(); 35 | } 36 | 37 | public TokenRespMsg(Integer code, String name, String msg) { 38 | this.code = code; 39 | this.name = name; 40 | this.msg = msg; 41 | } 42 | 43 | public Integer getCode() { 44 | return code; 45 | } 46 | 47 | public void setCode(Integer code) { 48 | this.code = code; 49 | } 50 | 51 | public String getMsg() { 52 | return msg; 53 | } 54 | 55 | public void setMsg(String msg) { 56 | this.msg = msg; 57 | } 58 | 59 | public String getName() { 60 | return name; 61 | } 62 | 63 | public void setName(String name) { 64 | this.name = name; 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return "TokenRespMsg{" + 70 | "code=" + code + 71 | ", name='" + name + '\'' + 72 | ", msg='" + msg + '\'' + 73 | '}'; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/impl/support/ProxiedFilterChain.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter.impl.support; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import javax.servlet.Filter; 7 | import javax.servlet.FilterChain; 8 | import javax.servlet.ServletException; 9 | import javax.servlet.ServletRequest; 10 | import javax.servlet.ServletResponse; 11 | import java.io.IOException; 12 | import java.util.List; 13 | 14 | /** 15 | * 代理的{@link FilterChain} 16 | * 17 | * @author zifangsky 18 | * @date 2019/5/9 19 | * @since 1.0.0 20 | */ 21 | public class ProxiedFilterChain implements FilterChain { 22 | private static final Logger LOGGER = LoggerFactory.getLogger(ProxiedFilterChain.class); 23 | 24 | /** 25 | * 原过滤链 26 | */ 27 | private FilterChain original; 28 | 29 | /** 30 | * 当前请求URL,在执行原过滤链之前需要执行的filter 31 | */ 32 | private List beforeFilters; 33 | 34 | /** 35 | * 用于计数 36 | */ 37 | private int index = 0; 38 | 39 | public ProxiedFilterChain(FilterChain original, List beforeFilters) { 40 | if(original == null){ 41 | throw new IllegalArgumentException("Parameter original cannot be empty."); 42 | } 43 | 44 | this.original = original; 45 | this.beforeFilters = beforeFilters; 46 | } 47 | 48 | @Override 49 | public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { 50 | if(this.beforeFilters == null || this.beforeFilters.size() == this.index){ 51 | LOGGER.debug("Now proceed to the original FilterChain."); 52 | this.original.doFilter(request, response); 53 | }else{ 54 | this.beforeFilters.get(this.index++).doFilter(request, response, this); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/enums/DefaultTokenRespEnums.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.enums; 2 | 3 | /** 4 | * 默认的几种基于Token的返回提示 5 | * 6 | * @author zifangsky 7 | * @date 2019/5/7 8 | * @since 1.0.0 9 | */ 10 | public enum DefaultTokenRespEnums { 11 | /** 12 | * 登录成功 13 | */ 14 | LOGIN_SUCCESS(200, "login_success", "登录成功!"), 15 | /** 16 | * 注销登录成功 17 | */ 18 | LOGOUT(200, "logout", "注销登录成功!"), 19 | 20 | /** 21 | * 登录失败 22 | */ 23 | LOGIN_FAILURE(401, "login_failure", "登录失败!"), 24 | 25 | /** 26 | * 未登录 27 | */ 28 | UN_LOGIN(401, "un_login", "您还未登录系统,无法访问该地址!"), 29 | /** 30 | * 被踢出 31 | */ 32 | KICKOUT(401, "kicked_out", "您的账号已在其他设备登录,若非本人操作,请立即重新登录并修改密码!"), 33 | /** 34 | * 没有权限 35 | */ 36 | NO_PERMISSIONS(403, "no_permissions", "您当前没有权限访问该地址!"), 37 | /** 38 | * 不可用的TOKEN 39 | */ 40 | INVALID_TOKEN(401, "invalid_token", "请求的Access Token或Refresh Token不可用!"), 41 | /** 42 | * TOKEN过期 43 | */ 44 | EXPIRED_TOKEN(403, "expired_token", "请求的Access Token或Refresh Token已过期!"), 45 | /** 46 | * 系统异常 47 | */ 48 | SYSTEM_ERROR(500, "system_error", "系统异常!") 49 | ; 50 | 51 | DefaultTokenRespEnums(int code, String name, String msg) { 52 | this.code = code; 53 | this.name = name; 54 | this.msg = msg; 55 | } 56 | 57 | /** 58 | * 返回状态码 59 | */ 60 | private int code; 61 | 62 | /** 63 | * 返回字段名 64 | */ 65 | private String name; 66 | 67 | /** 68 | * 返回提示信息 69 | */ 70 | private String msg; 71 | 72 | public int getCode() { 73 | return code; 74 | } 75 | 76 | public String getName() { 77 | return name; 78 | } 79 | 80 | public String getMsg() { 81 | return msg; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/access/impl/support/AccessCallable.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.access.impl.support; 2 | 3 | import cn.zifangsky.easylimit.access.Access; 4 | import cn.zifangsky.easylimit.common.BindOperator; 5 | 6 | import java.util.concurrent.Callable; 7 | 8 | /** 9 | * 在过滤链执行之前,做一些绑定操作 10 | * 11 | * @author zifangsky 12 | * @date 2019/4/4 13 | * @since 1.0.0 14 | */ 15 | public class AccessCallable implements Callable { 16 | /** 17 | * 绑定逻辑 18 | */ 19 | private BindOperator bindOperator; 20 | 21 | /** 22 | * 实际的线程执行逻辑 23 | */ 24 | private Callable callable; 25 | 26 | public AccessCallable(Access access, Callable callable){ 27 | this(new AccessBindOperator(access), callable); 28 | } 29 | 30 | protected AccessCallable(BindOperator bindOperator, Callable callable) { 31 | if (bindOperator == null){ 32 | throw new IllegalArgumentException("Parameter bindOperator cannot be empty."); 33 | } 34 | if (callable == null){ 35 | throw new IllegalArgumentException("Parameter callable cannot be empty."); 36 | } 37 | 38 | this.bindOperator = bindOperator; 39 | this.callable = callable; 40 | } 41 | 42 | @Override 43 | public T call() throws Exception { 44 | try { 45 | //1. 绑定 46 | this.bindOperator.bind(); 47 | //2. 继续执行线程的逻辑 48 | return this.doCall(this.callable); 49 | }finally { 50 | //3. 恢复状态 51 | this.bindOperator.recovery(); 52 | } 53 | } 54 | 55 | /** 56 | * 实际的线程执行逻辑 57 | * @author zifangsky 58 | * @date 2019/4/4 17:12 59 | * @since 1.0.0 60 | */ 61 | protected T doCall(Callable callable) throws Exception { 62 | return callable.call(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/access/impl/TokenAccessFactory.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.access.impl; 2 | 3 | import cn.zifangsky.easylimit.TokenWebSecurityManager; 4 | import cn.zifangsky.easylimit.access.Access; 5 | import cn.zifangsky.easylimit.access.AccessContext; 6 | import cn.zifangsky.easylimit.access.AccessFactory; 7 | import cn.zifangsky.easylimit.authc.PrincipalInfo; 8 | import cn.zifangsky.easylimit.session.Session; 9 | import cn.zifangsky.easylimit.session.impl.support.SimpleAccessToken; 10 | import cn.zifangsky.easylimit.session.impl.support.SimpleRefreshToken; 11 | 12 | import javax.servlet.ServletRequest; 13 | import javax.servlet.ServletResponse; 14 | 15 | /** 16 | * 基于token模式的{@link AccessFactory} 17 | * 18 | * @author zifangsky 19 | * @date 2019/4/8 20 | * @since 1.0.0 21 | */ 22 | public class TokenAccessFactory implements AccessFactory { 23 | 24 | @Override 25 | public Access createAccess(AccessContext accessContext) { 26 | TokenAccessContext tokenAccessContext = (TokenAccessContext) accessContext; 27 | 28 | ServletRequest request = tokenAccessContext.acquireServletRequest(); 29 | ServletResponse response = tokenAccessContext.acquireServletResponse(); 30 | String host = tokenAccessContext.acquireHost(); 31 | Session session = tokenAccessContext.acquireSession(); 32 | boolean authenticated = tokenAccessContext.acquireAuthenticated(); 33 | PrincipalInfo principalInfo = tokenAccessContext.acquirePrincipalInfo(); 34 | TokenWebSecurityManager securityManager = (TokenWebSecurityManager) tokenAccessContext.acquireSecurityManager(); 35 | SimpleAccessToken accessToken = tokenAccessContext.acquireAccessToken(); 36 | SimpleRefreshToken refreshToken = tokenAccessContext.acquireRefreshToken(); 37 | 38 | return new ExposedTokenAccess(request, response, host, session, authenticated, principalInfo, securityManager, accessToken, refreshToken); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/support/DefaultCacheSessionDAO.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl.support; 2 | 3 | import cn.zifangsky.easylimit.cache.Cache; 4 | import cn.zifangsky.easylimit.exception.session.UnknownSessionException; 5 | import cn.zifangsky.easylimit.session.Session; 6 | import cn.zifangsky.easylimit.session.SessionDAO; 7 | 8 | import java.io.Serializable; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | /** 12 | * 默认实现的缓存{@link SessionDAO} 13 | * 14 | * @author zifangsky 15 | * @date 2019/4/2 16 | * @since 1.0.0 17 | */ 18 | public class DefaultCacheSessionDAO extends AbstractCacheSessionDAO { 19 | /** 20 | * 使用{@link ConcurrentHashMap}存储所有本地{@link Session} 21 | */ 22 | private ConcurrentHashMap sessionStorageMap; 23 | 24 | public DefaultCacheSessionDAO(Cache cache) { 25 | super(cache); 26 | this.sessionStorageMap = new ConcurrentHashMap<>(); 27 | } 28 | 29 | public DefaultCacheSessionDAO(Cache cache, String sessionCacheName) { 30 | super(cache, sessionCacheName); 31 | this.sessionStorageMap = new ConcurrentHashMap<>(); 32 | } 33 | 34 | public DefaultCacheSessionDAO(Cache cache, ConcurrentHashMap sessionStorageMap) { 35 | super(cache); 36 | this.sessionStorageMap = sessionStorageMap; 37 | } 38 | 39 | @Override 40 | protected Session doRead(Serializable sessionId) throws UnknownSessionException { 41 | return sessionStorageMap.get(sessionId); 42 | } 43 | 44 | @Override 45 | protected void doUpdate(Serializable sessionId, Session session) throws UnknownSessionException { 46 | sessionStorageMap.put(sessionId, session); 47 | } 48 | 49 | @Override 50 | protected void doDelete(Session session) throws UnknownSessionException { 51 | sessionStorageMap.remove(session.getId()); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/authc/impl/SimplePrincipalInfo.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.authc.impl; 2 | 3 | import cn.zifangsky.easylimit.authc.PrincipalInfo; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | 6 | /** 7 | * {@link PrincipalInfo}的基本实现 8 | *

Note: 登录校验时正确的用户名、密码

9 | * 10 | * @author zifangsky 11 | * @date 2019/4/3 12 | * @since 1.0.0 13 | */ 14 | public class SimplePrincipalInfo implements PrincipalInfo { 15 | private static final long serialVersionUID = 8416222905146081056L; 16 | 17 | /** 18 | * 用户主体 19 | */ 20 | private Object principal; 21 | 22 | /** 23 | * 用户名 24 | */ 25 | private String username; 26 | 27 | /** 28 | * 密码 29 | */ 30 | private String password; 31 | 32 | public SimplePrincipalInfo() { 33 | 34 | } 35 | 36 | public SimplePrincipalInfo(Object principal, String username, String password) { 37 | this.principal = principal; 38 | this.username = username; 39 | this.password = password; 40 | } 41 | 42 | public String getUsername() { 43 | return username; 44 | } 45 | 46 | public void setUsername(String username) { 47 | this.username = username; 48 | } 49 | 50 | @Override 51 | public String getPassword() { 52 | return password; 53 | } 54 | 55 | public void setPassword(String password) { 56 | this.password = password; 57 | } 58 | 59 | @Override 60 | @JsonIgnore 61 | public String getAccount() { 62 | return this.username; 63 | } 64 | 65 | @Override 66 | public Object getPrincipal() { 67 | return this.principal; 68 | } 69 | 70 | public void setPrincipal(Object principal) { 71 | this.principal = principal; 72 | } 73 | 74 | @Override 75 | public String toString() { 76 | return "SimplePrincipalInfo{" + 77 | "username='" + username + '\'' + 78 | ", password='it doesn't show up here'" + '\'' + 79 | '}'; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/TokenOperateResolver.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session; 2 | 3 | import cn.zifangsky.easylimit.authc.PrincipalInfo; 4 | import cn.zifangsky.easylimit.authc.ValidatedInfo; 5 | import cn.zifangsky.easylimit.session.impl.support.SimpleAccessToken; 6 | import cn.zifangsky.easylimit.session.impl.support.SimpleRefreshToken; 7 | import cn.zifangsky.easylimit.session.impl.support.TokenInfo; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * Token的几种基本操作 13 | * 14 | * @author zifangsky 15 | * @date 2019/6/4 16 | * @since 1.0.0 17 | */ 18 | public interface TokenOperateResolver { 19 | 20 | /** 21 | * 创建Access Token 22 | * @author zifangsky 23 | * @date 2019/6/4 16:42 24 | * @since 1.0.0 25 | * @param principalInfo 用户主体 26 | * @param tokenInfo 用于获取有效期 27 | * @param sessionId sessionId 28 | * @return cn.zifangsky.easylimit.session.impl.support.SimpleAccessToken 29 | */ 30 | SimpleAccessToken createAccessToken(PrincipalInfo principalInfo, TokenInfo tokenInfo, Serializable sessionId); 31 | 32 | /** 33 | * 创建Refresh Token 34 | * @author zifangsky 35 | * @date 2019/6/4 16:42 36 | * @since 1.0.0 37 | * @param validatedInfo 登录信息 38 | * @param tokenInfo 用于获取有效期 39 | * @param accessToken Access Token 40 | * @return cn.zifangsky.easylimit.session.impl.support.SimpleRefreshToken 41 | */ 42 | SimpleRefreshToken createRefreshToken(ValidatedInfo validatedInfo, TokenInfo tokenInfo, String accessToken); 43 | 44 | /** 45 | * 校验某个Access Token是否仍然有效 46 | * @author zifangsky 47 | * @date 2019/6/4 14:12 48 | * @since 1.0.0 49 | * @param simpleAccessToken Access Token 50 | * @return boolean 51 | */ 52 | boolean isValid(SimpleAccessToken simpleAccessToken); 53 | 54 | /** 55 | * 校验某个Refresh Token是否仍然有效 56 | * @author zifangsky 57 | * @date 2019/6/4 14:12 58 | * @since 1.0.0 59 | * @param simpleRefreshToken Refresh Token 60 | * @return boolean 61 | */ 62 | boolean isValid(SimpleRefreshToken simpleRefreshToken); 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/TokenSessionContext.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl; 2 | 3 | import cn.zifangsky.easylimit.session.SessionContext; 4 | import cn.zifangsky.easylimit.session.impl.support.SimpleAccessToken; 5 | import cn.zifangsky.easylimit.session.impl.support.SimpleRefreshToken; 6 | 7 | import java.io.Serializable; 8 | import java.util.Map; 9 | 10 | /** 11 | * 基于Token模式的{@link SessionContext} 12 | * 13 | * @author zifangsky 14 | * @date 2019/6/3 15 | * @see SessionContext 16 | * @since 1.0.0 17 | */ 18 | public class TokenSessionContext extends DefaultSessionContext { 19 | private static final long serialVersionUID = 6297415889146147706L; 20 | 21 | /** 22 | * {@link SimpleAccessToken}在session中的key 23 | */ 24 | public static final String SIMPLE_ACCESS_TOKEN_KEY = TokenSessionContext.class.getName() + ":simple_access_token"; 25 | 26 | /** 27 | * {@link SimpleRefreshToken}在session中的key 28 | */ 29 | public static final String SIMPLE_REFRESH_TOKEN_KEY = TokenSessionContext.class.getName() + ":simple_refresh_token"; 30 | 31 | 32 | public TokenSessionContext() { 33 | this(null, null); 34 | } 35 | 36 | public TokenSessionContext(String host) { 37 | this(host, null); 38 | } 39 | 40 | public TokenSessionContext(String host, Serializable sessionId) { 41 | super(); 42 | 43 | if(host != null){ 44 | this.setHost(host); 45 | } 46 | if(sessionId != null){ 47 | this.setSessionId(sessionId); 48 | } 49 | } 50 | 51 | public TokenSessionContext(Map map) { 52 | super(map); 53 | } 54 | 55 | 56 | public SimpleAccessToken getSimpleAccessToken() { 57 | return getByType(SIMPLE_ACCESS_TOKEN_KEY, SimpleAccessToken.class); 58 | } 59 | 60 | public void setSimpleAccessToken(SimpleAccessToken accessToken) { 61 | put(SIMPLE_ACCESS_TOKEN_KEY, accessToken); 62 | } 63 | 64 | public SimpleRefreshToken getSimpleRefreshToken() { 65 | return getByType(SIMPLE_REFRESH_TOKEN_KEY, SimpleRefreshToken.class); 66 | } 67 | 68 | public void setSimpleRefreshToken(SimpleRefreshToken refreshToken) { 69 | put(SIMPLE_REFRESH_TOKEN_KEY, refreshToken); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/common/Constants.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.common; 2 | 3 | /** 4 | * 公共常量类 5 | * 6 | * @author zifangsky 7 | * @date 2019/4/1 8 | * @since 1.0.0 9 | */ 10 | public class Constants { 11 | /** 12 | * 项目名 13 | */ 14 | public static final String PROJECT_NAME = "easylimit"; 15 | 16 | /** 17 | * session校验的线程名 18 | */ 19 | public static final String SESSION_CHECK_THREAD_NAME = "session_check"; 20 | 21 | /** 22 | * 默认sessionId在cookie中的名称 23 | */ 24 | public static final String DEFAULT_COOKIE_SESSION_ID_NAME = PROJECT_NAME + "_session_id"; 25 | 26 | /** 27 | * 表示当前session已经被“踢出”的标识的参数名 28 | */ 29 | public static final String KICK_OUT_OLD_SESSIONS_NAME = PROJECT_NAME + "_kicked_out"; 30 | 31 | /** 32 | * “踢出”的URL参数名 33 | */ 34 | public static final String KICK_OUT_OLD_SESSIONS_PARAM_NAME = "kicked_out"; 35 | 36 | /** 37 | * Ajax请求的Header 38 | */ 39 | public static final String AJAX_REQUEST_HEADER = "XMLHttpRequest"; 40 | 41 | /** 42 | * 保存的请求来源的URL的参数名称 43 | */ 44 | public static final String SAVED_SOURCE_URL_NAME = "saved_source_url"; 45 | 46 | /** 47 | * 回调URL的参数名称 48 | */ 49 | public static final String DEFAULT_REDIRECT_URL_NAME = "redirect_url"; 50 | 51 | /** 52 | * 默认的登录URL 53 | */ 54 | public static final String DEFAULT_LOGIN_URL = "/login.html"; 55 | 56 | /** 57 | * 默认的登录校验URL 58 | */ 59 | public static final String DEFAULT_LOGIN_CHECK_URL = "/check"; 60 | 61 | /** 62 | * 默认的未授权URL 63 | */ 64 | public static final String DEFAULT_UNAUTHORIZED_URL = "/error.html"; 65 | 66 | /** 67 | * 默认的注销之后的重定向URL 68 | */ 69 | public static final String DEFAULT_LOGOUT_REDIRECT_URL = DEFAULT_LOGIN_URL; 70 | 71 | 72 | /** 73 | * 默认的Access Token的参数名 74 | */ 75 | public static final String DEFAULT_ACCESS_TOKEN_PARAM_NAME = "access_token"; 76 | 77 | /** 78 | * 默认的Refresh Token的参数名 79 | */ 80 | public static final String DEFAULT_REFRESH_TOKEN_PARAM_NAME = "refresh_token"; 81 | 82 | /** 83 | * 默认的Access Token的过期时间的参数名 84 | */ 85 | public static final String DEFAULT_EXPIRES_IN_PARAM_NAME = "expires_in"; 86 | 87 | 88 | 89 | 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/SessionContext.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session; 2 | 3 | import javax.servlet.ServletRequest; 4 | import javax.servlet.ServletResponse; 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 用于存储{@link Session}的初始数据 9 | * 10 | * @author zifangsky 11 | * @date 2019/3/25 12 | * @since 1.0.0 13 | */ 14 | public interface SessionContext { 15 | 16 | /** 17 | * 获取Host 18 | * 19 | * @return java.lang.String 20 | * @author zifangsky 21 | * @date 2019/3/25 17:34 22 | * @since 1.0.0 23 | */ 24 | String getHost(); 25 | 26 | /** 27 | * 设置Host 28 | * 29 | * @param host host 30 | * @author zifangsky 31 | * @date 2019/3/25 17:34 32 | * @since 1.0.0 33 | */ 34 | void setHost(String host); 35 | 36 | /** 37 | * 获取SessionId 38 | * 39 | * @return java.io.Serializable 40 | * @author zifangsky 41 | * @date 2019/3/25 17:35 42 | * @since 1.0.0 43 | */ 44 | Serializable getSessionId(); 45 | 46 | /** 47 | * 设置SessionId 48 | * 49 | * @param sessionId sessionId 50 | * @author zifangsky 51 | * @date 2019/3/25 17:35 52 | * @since 1.0.0 53 | */ 54 | void setSessionId(Serializable sessionId); 55 | 56 | /** 57 | * 获取{@link ServletRequest} 58 | * 59 | * @return javax.servlet.ServletRequest 60 | * @author zifangsky 61 | * @date 2019/3/26 12:29 62 | * @since 1.0.0 63 | */ 64 | ServletRequest getServletRequest(); 65 | 66 | /** 67 | * 设置{@link ServletRequest} 68 | * 69 | * @param request ServletRequest 70 | * @author zifangsky 71 | * @date 2019/3/26 12:29 72 | * @since 1.0.0 73 | */ 74 | void setServletRequest(ServletRequest request); 75 | 76 | /** 77 | * 获取{@link ServletResponse} 78 | * 79 | * @return javax.servlet.ServletResponse 80 | * @author zifangsky 81 | * @date 2019/3/26 12:30 82 | * @since 1.0.0 83 | */ 84 | ServletResponse getServletResponse(); 85 | 86 | /** 87 | * 设置{@link ServletResponse} 88 | * 89 | * @param response ServletResponse 90 | * @author zifangsky 91 | * @date 2019/3/26 12:30 92 | * @since 1.0.0 93 | */ 94 | void setServletResponse(ServletResponse response); 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/authc/impl/UsernamePasswordValidatedInfo.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.authc.impl; 2 | 3 | import cn.zifangsky.easylimit.authc.ValidatedInfo; 4 | import cn.zifangsky.easylimit.enums.EncryptionTypeEnums; 5 | import com.fasterxml.jackson.annotation.JsonIgnore; 6 | 7 | /** 8 | * 基于“用户名+密码”的登录验证信息 9 | * 10 | * @author zifangsky 11 | * @date 2019/4/3 12 | * @since 1.0.0 13 | */ 14 | public class UsernamePasswordValidatedInfo implements ValidatedInfo { 15 | private static final long serialVersionUID = 5329929348382496608L; 16 | 17 | /** 18 | * 用户名 19 | */ 20 | private String username; 21 | 22 | /** 23 | * 密码 24 | */ 25 | private String password; 26 | 27 | /** 28 | * 密码的加密方式 29 | */ 30 | private EncryptionTypeEnums encryptionType; 31 | 32 | public UsernamePasswordValidatedInfo() { 33 | } 34 | 35 | public UsernamePasswordValidatedInfo(String username, String password, EncryptionTypeEnums encryptionType) { 36 | this.username = username; 37 | this.password = password; 38 | this.encryptionType = encryptionType; 39 | } 40 | 41 | @Override 42 | @JsonIgnore 43 | public String getSubject() { 44 | return this.username; 45 | } 46 | 47 | @Override 48 | @JsonIgnore 49 | public String getCredentials() { 50 | return this.password; 51 | } 52 | 53 | public EncryptionTypeEnums getEncryptionType() { 54 | return encryptionType; 55 | } 56 | 57 | public String getUsername() { 58 | return username; 59 | } 60 | 61 | public String getPassword() { 62 | return password; 63 | } 64 | 65 | public void setUsername(String username) { 66 | this.username = username; 67 | } 68 | 69 | public void setPassword(String password) { 70 | this.password = password; 71 | } 72 | 73 | public void setEncryptionType(EncryptionTypeEnums encryptionType) { 74 | this.encryptionType = encryptionType; 75 | } 76 | 77 | @Override 78 | public String toString() { 79 | return "UsernamePasswordValidatedInfo{" + 80 | "username='" + username + '\'' + 81 | ", password='it doesn't show up here'" + '\'' + 82 | ", encryptionType=" + encryptionType + 83 | '}'; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/MemorySessionDAO.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl; 2 | 3 | import cn.zifangsky.easylimit.exception.session.UnknownSessionException; 4 | import cn.zifangsky.easylimit.session.Session; 5 | import cn.zifangsky.easylimit.session.SessionDAO; 6 | 7 | import java.io.Serializable; 8 | import java.util.Collection; 9 | import java.util.HashSet; 10 | import java.util.Set; 11 | import java.util.concurrent.ConcurrentHashMap; 12 | 13 | /** 14 | * 基于内存存储的{@link SessionDAO} 15 | * 16 | * @author zifangsky 17 | * @date 2019/4/1 18 | * @since 1.0.0 19 | */ 20 | public class MemorySessionDAO implements SessionDAO { 21 | /** 22 | * 使用{@link ConcurrentHashMap}存储所有{@link Session} 23 | */ 24 | private ConcurrentHashMap sessionStorageMap; 25 | 26 | public MemorySessionDAO() { 27 | this.sessionStorageMap = new ConcurrentHashMap<>(); 28 | } 29 | 30 | public MemorySessionDAO(ConcurrentHashMap sessionStorageMap) { 31 | this.sessionStorageMap = sessionStorageMap; 32 | } 33 | 34 | @Override 35 | public Session read(Serializable sessionId) throws UnknownSessionException { 36 | if (sessionId == null) { 37 | throw new IllegalArgumentException("Parameter sessionId cannot be empty."); 38 | } 39 | 40 | return sessionStorageMap.get(sessionId); 41 | } 42 | 43 | @Override 44 | public void update(Session session) throws UnknownSessionException { 45 | if (session == null) { 46 | throw new IllegalArgumentException("Parameter session cannot be empty."); 47 | } 48 | 49 | sessionStorageMap.put(session.getId(), session); 50 | } 51 | 52 | @Override 53 | public void delete(Session session) { 54 | if (session == null) { 55 | throw new IllegalArgumentException("Parameter session cannot be empty."); 56 | } 57 | 58 | sessionStorageMap.remove(session.getId()); 59 | } 60 | 61 | @Override 62 | public Set getActiveSessions() { 63 | Collection values = sessionStorageMap.values(); 64 | Set result = new HashSet<>(); 65 | 66 | if (values != null && values.size() > 0) { 67 | result.addAll(values); 68 | } 69 | 70 | return result; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/permission/aop/DefaultAnnotationMethodInterceptor.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.permission.aop; 2 | 3 | import cn.zifangsky.easylimit.exception.authc.AuthenticationException; 4 | import org.aopalliance.intercept.MethodInterceptor; 5 | import org.aopalliance.intercept.MethodInvocation; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * 默认实现的处理权限注解的{@link MethodInterceptor} 12 | * 13 | * @author zifangsky 14 | * @date 2019/6/19 15 | * @since 1.0.0 16 | */ 17 | public class DefaultAnnotationMethodInterceptor extends AbstractAnnotationMethodInterceptor { 18 | 19 | protected List annotationResolverList; 20 | 21 | public DefaultAnnotationMethodInterceptor() { 22 | this(new ArrayList<>()); 23 | } 24 | 25 | public DefaultAnnotationMethodInterceptor(List annotationResolverList) { 26 | if(annotationResolverList == null){ 27 | throw new IllegalArgumentException("Parameter annotationResolverList cannot be empty."); 28 | } 29 | 30 | this.annotationResolverList = annotationResolverList; 31 | //添加几个默认的注解处理器 32 | this.addDefaultAnnotationResolvers(); 33 | } 34 | 35 | @Override 36 | protected void assertPermission(MethodInvocation invocation) throws AuthenticationException { 37 | if(this.annotationResolverList.size() > 0){ 38 | for(AbstractAnnotationResolver resolver : this.annotationResolverList){ 39 | if(resolver.support(invocation)){ 40 | resolver.assertPermission(invocation); 41 | } 42 | } 43 | } 44 | } 45 | 46 | /** 47 | * 添加几个默认的注解处理器 48 | */ 49 | protected void addDefaultAnnotationResolvers(){ 50 | this.annotationResolverList.add(new LoginAnnotationResolver()); 51 | this.annotationResolverList.add(new RolesAnnotationResolver()); 52 | this.annotationResolverList.add(new PermissionsAnnotationResolver()); 53 | } 54 | 55 | public List getAnnotationResolverList() { 56 | return annotationResolverList; 57 | } 58 | 59 | public void setAnnotationResolverList(List annotationResolverList) { 60 | this.annotationResolverList = annotationResolverList; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/SecurityManager.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit; 2 | 3 | import cn.zifangsky.easylimit.access.Access; 4 | import cn.zifangsky.easylimit.access.AccessContext; 5 | import cn.zifangsky.easylimit.authc.ValidatedInfo; 6 | import cn.zifangsky.easylimit.exception.authc.AuthenticationException; 7 | import cn.zifangsky.easylimit.realm.Realm; 8 | import cn.zifangsky.easylimit.session.Session; 9 | import cn.zifangsky.easylimit.session.SessionContext; 10 | import cn.zifangsky.easylimit.session.SessionKey; 11 | 12 | /** 13 | * 认证、权限、session等管理的入口 14 | * 15 | * @author zifangsky 16 | * @date 2019/4/4 17 | * @since 1.0.0 18 | */ 19 | public interface SecurityManager extends Realm{ 20 | 21 | /** 22 | * 登录认证 23 | * 24 | * @author zifangsky 25 | * @date 2019/4/3 16:25 26 | * @param access 当前的请求信息 27 | * @param validatedInfo 待验证的用户名、密码 28 | * @since 1.0.0 29 | * @throws AuthenticationException AuthenticationException 30 | */ 31 | Access login(Access access, ValidatedInfo validatedInfo) throws AuthenticationException; 32 | 33 | /** 34 | * 注销登录 35 | * 36 | * @param access 当前的请求信息 37 | * @author zifangsky 38 | * @date 2019/4/3 16:29 39 | * @since 1.0.0 40 | */ 41 | void logout(Access access); 42 | 43 | /** 44 | * 通过{@link AccessContext}创建{@link Access} 45 | * @author zifangsky 46 | * @date 2019/4/4 15:19 47 | * @since 1.0.0 48 | * @param accessContext accessContext 49 | * @return cn.zifangsky.easylimit.access.Access 50 | */ 51 | Access createAccess(AccessContext accessContext) throws Exception; 52 | 53 | /** 54 | * 通过{@link SessionKey}获取{@link Session} 55 | * 56 | * @param key SessionKey 57 | * @param accessContext 当前请求的环境变量 58 | * @return cn.zifangsky.easylimit.session.Session 59 | * @author zifangsky 60 | * @date 2019/3/29 11:49 61 | * @since 1.0.0 62 | */ 63 | Session getSession(SessionKey key, AccessContext accessContext); 64 | 65 | /** 66 | * 通过{@link SessionContext}创建{@link Session} 67 | * 68 | * @param sessionContext sessionContext 69 | * @return cn.zifangsky.easylimit.session.Session 70 | * @author zifangsky 71 | * @date 2019/3/29 15:17 72 | * @since 1.0.0 73 | */ 74 | Session createSession(SessionContext sessionContext); 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/AbstractVerifyFilter.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter; 2 | 3 | import cn.zifangsky.easylimit.access.Access; 4 | import cn.zifangsky.easylimit.common.Constants; 5 | import cn.zifangsky.easylimit.enums.DefaultTokenRespEnums; 6 | import cn.zifangsky.easylimit.enums.ProjectModeEnums; 7 | import cn.zifangsky.easylimit.filter.impl.support.TokenRespMsg; 8 | import cn.zifangsky.easylimit.utils.WebUtils; 9 | 10 | import javax.servlet.Filter; 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | 14 | /** 15 | * 角色权限相关校验的{@link Filter} 16 | * 17 | * @author zifangsky 18 | * @date 2019/5/7 19 | * @since 1.0.0 20 | */ 21 | public abstract class AbstractVerifyFilter extends AbstractAccessControlFilter{ 22 | 23 | /** 24 | * Token模式的没有权限情况的错误返回 25 | */ 26 | private TokenRespMsg unauthorizedRespMsg = new TokenRespMsg(DefaultTokenRespEnums.NO_PERMISSIONS); 27 | 28 | /** 29 | * 认证失败跳转的URL 30 | */ 31 | private String unauthorizedUrl = Constants.DEFAULT_UNAUTHORIZED_URL; 32 | 33 | @Override 34 | protected boolean afterAccessDenied(HttpServletRequest request, HttpServletResponse response, String[] controlVal) throws Exception { 35 | if(ProjectModeEnums.DEFAULT.equals(this.getProjectMode()) && !WebUtils.isAjaxRequest(request)){ 36 | Access access = this.getAccess(request, response); 37 | 38 | //如果还没有登录,则跳转到登录页面 39 | if(access != null && !access.isAuthenticated()){ 40 | this.saveSourceUrlAndRedirectToLoginPage(request, response, null); 41 | }else{ 42 | this.doRedirect(request, response, this.unauthorizedUrl, null); 43 | } 44 | }else{ 45 | this.generateTokenResponse(response, this.unauthorizedRespMsg); 46 | } 47 | 48 | return false; 49 | } 50 | 51 | public String getUnauthorizedUrl() { 52 | return unauthorizedUrl; 53 | } 54 | 55 | public void setUnauthorizedUrl(String unauthorizedUrl) { 56 | this.unauthorizedUrl = unauthorizedUrl; 57 | } 58 | 59 | public TokenRespMsg getUnauthorizedRespMsg() { 60 | return unauthorizedRespMsg; 61 | } 62 | 63 | public void setUnauthorizedRespMsg(TokenRespMsg unauthorizedRespMsg) { 64 | this.unauthorizedRespMsg = unauthorizedRespMsg; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/access/impl/support/AccessBindOperator.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.access.impl.support; 2 | 3 | import cn.zifangsky.easylimit.access.Access; 4 | import cn.zifangsky.easylimit.common.BindOperator; 5 | import cn.zifangsky.easylimit.SecurityManager; 6 | import cn.zifangsky.easylimit.utils.SecurityUtils; 7 | import cn.zifangsky.easylimit.utils.ThreadContext; 8 | 9 | import java.util.Map; 10 | 11 | /** 12 | * 用于将{@link Access}和{@link SecurityManager}绑定到ThreadLocal 13 | * 14 | * @author zifangsky 15 | * @date 2019/4/4 16 | * @since 1.0.0 17 | */ 18 | public class AccessBindOperator implements BindOperator { 19 | 20 | private Access access; 21 | private SecurityManager securityManager; 22 | /** 23 | * 绑定之前的数据,用于请求结束之后恢复之前状态 24 | *

Note: 目的是避免污染线程池中线程的{@link ThreadLocal}数据

25 | */ 26 | private Map originalResources; 27 | 28 | 29 | public AccessBindOperator(Access access) { 30 | if (access == null){ 31 | throw new IllegalArgumentException("Parameter access cannot be empty."); 32 | } 33 | 34 | this.access = access; 35 | 36 | SecurityManager securityManager = access.getSecurityManager(); 37 | 38 | //如果为空,则获取备份的SecurityManager 39 | if(securityManager == null){ 40 | securityManager = SecurityUtils.getSecurityManager(); 41 | } 42 | 43 | this.securityManager = securityManager; 44 | } 45 | 46 | /** 47 | * 实际的绑定操作 48 | * @author zifangsky 49 | * @date 2019/4/4 17:10 50 | * @since 1.0.0 51 | */ 52 | @Override 53 | public void bind(){ 54 | //1. 获取绑定之前的线程中的ThreadLocal数据 55 | this.originalResources = ThreadContext.getResources(); 56 | 57 | //2.清空线程中的ThreadLocal数据 58 | ThreadContext.remove(); 59 | 60 | //3. 绑定Access 61 | ThreadContext.bindAccess(this.access); 62 | //4. 绑定SecurityManager 63 | if(this.securityManager != null){ 64 | ThreadContext.bindSecurityManager(this.securityManager); 65 | } 66 | } 67 | 68 | @Override 69 | public void recovery(){ 70 | //1. 清空 71 | ThreadContext.remove(); 72 | 73 | //2. 恢复 74 | if(this.originalResources != null && this.originalResources.size() > 0){ 75 | ThreadContext.setResources(originalResources); 76 | } 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/TokenDAO.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session; 2 | 3 | import cn.zifangsky.easylimit.session.impl.support.SimpleAccessToken; 4 | import cn.zifangsky.easylimit.session.impl.support.SimpleRefreshToken; 5 | 6 | /** 7 | * token存储 8 | * 9 | * @author zifangsky 10 | * @date 2019/6/3 11 | * @since 1.0.0 12 | */ 13 | public interface TokenDAO { 14 | /** 15 | * 通过Access Token获取{@link SimpleAccessToken} 16 | * @author zifangsky 17 | * @date 2019/6/3 14:57 18 | * @since 1.0.0 19 | * @param accessToken Access Token 20 | * @return cn.zifangsky.easylimit.session.impl.support.SimpleAccessToken 21 | */ 22 | SimpleAccessToken readByAccessToken(String accessToken); 23 | 24 | /** 25 | * 通Refresh Token获取{@link SimpleRefreshToken} 26 | * @author zifangsky 27 | * @date 2019/6/3 14:57 28 | * @since 1.0.0 29 | * @param refreshToken Refresh Token 30 | * @return cn.zifangsky.easylimit.session.impl.support.SimpleAccessToken 31 | */ 32 | SimpleRefreshToken readByRefreshToken(String refreshToken); 33 | 34 | /** 35 | * 更新Access Token 36 | * @author zifangsky 37 | * @date 2019/6/3 15:02 38 | * @since 1.0.0 39 | * @param simpleAccessToken Access Token实例 40 | */ 41 | void updateAccessToken(SimpleAccessToken simpleAccessToken); 42 | 43 | /** 44 | * 更新Refresh Token 45 | * @author zifangsky 46 | * @date 2019/6/3 15:02 47 | * @since 1.0.0 48 | * @param refreshToken Refresh Token实例 49 | */ 50 | void updateRefreshToken(SimpleRefreshToken refreshToken); 51 | 52 | /** 53 | * 删除指定的Access Token 54 | * @author zifangsky 55 | * @date 2019/6/3 15:06 56 | * @since 1.0.0 57 | * @param accessToken Access Token 58 | */ 59 | void deleteAccessToken(String accessToken); 60 | 61 | /** 62 | * 删除指定的Refresh Token 63 | * @author zifangsky 64 | * @date 2019/6/3 15:06 65 | * @since 1.0.0 66 | * @param refreshToken Refresh Token 67 | */ 68 | void deleteRefreshToken(String refreshToken); 69 | 70 | /** 71 | * 删除某个用户的所有历史Access Token 72 | * @author zifangsky 73 | * @date 2019/6/14 17:18 74 | * @since 1.0.0 75 | * @param account 账户标识 76 | */ 77 | void deleteOldAccessToken(String account); 78 | 79 | /** 80 | * 删除某个用户的所有历史Refresh Token 81 | * @author zifangsky 82 | * @date 2019/6/14 17:20 83 | * @since 1.0.0 84 | * @param accessToken Access Token 85 | */ 86 | void deleteOldRefreshToken(String accessToken); 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/impl/support/DefaultProxiedFilter.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter.impl.support; 2 | 3 | import cn.zifangsky.easylimit.SecurityManager; 4 | import cn.zifangsky.easylimit.access.Access; 5 | import cn.zifangsky.easylimit.filter.FilterChainResolver; 6 | import cn.zifangsky.easylimit.session.Session; 7 | import cn.zifangsky.easylimit.utils.SecurityUtils; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import javax.servlet.FilterChain; 12 | import javax.servlet.ServletException; 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.servlet.http.HttpServletResponse; 15 | import java.io.IOException; 16 | 17 | /** 18 | * 默认的{@link AbstractProxiedFilter} 19 | * 20 | * @author zifangsky 21 | * @date 2019/5/15 22 | * @since 1.0.0 23 | */ 24 | public class DefaultProxiedFilter extends AbstractProxiedFilter { 25 | private static final Logger LOGGER = LoggerFactory.getLogger(DefaultProxiedFilter.class); 26 | 27 | public DefaultProxiedFilter(SecurityManager securityManager, FilterChainResolver filterChainResolver) { 28 | super(securityManager, filterChainResolver); 29 | } 30 | 31 | @Override 32 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain originalChain) throws ServletException, IOException { 33 | try { 34 | //1. 创建访问实例 35 | Access access = this.createAccess(request, response); 36 | 37 | //2. 执行过滤链 38 | access.execute(() -> { 39 | //2.1 更新session的访问时间 40 | updateSessionLatestAccessTime(request); 41 | 42 | //2.2 执行过滤链 43 | executeFilterChain(request, response, originalChain); 44 | 45 | return null; 46 | }); 47 | }catch (Exception e){ 48 | if (e instanceof IOException) { 49 | throw (IOException) e; 50 | }else{ 51 | throw new ServletException(e); 52 | } 53 | } 54 | } 55 | 56 | /** 57 | * 刷新session的最新访问时间 58 | * @author zifangsky 59 | * @date 2019/5/16 14:19 60 | * @since 1.0.0 61 | * @param request HttpServletRequest 62 | */ 63 | protected void updateSessionLatestAccessTime(HttpServletRequest request){ 64 | Access access = SecurityUtils.getAccess(); 65 | Session session = access.getSession(false); 66 | 67 | if(session != null){ 68 | try { 69 | session.refresh(); 70 | }catch (Exception e){ 71 | LOGGER.error("The session.refresh() method failed to execute.", e); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/access/impl/TokenAccessContext.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.access.impl; 2 | 3 | import cn.zifangsky.easylimit.access.AccessContext; 4 | import cn.zifangsky.easylimit.session.Session; 5 | import cn.zifangsky.easylimit.session.impl.TokenSessionContext; 6 | import cn.zifangsky.easylimit.session.impl.support.SimpleAccessToken; 7 | import cn.zifangsky.easylimit.session.impl.support.SimpleRefreshToken; 8 | 9 | /** 10 | * 基于token模式的{@link AccessContext} 11 | * 12 | * @author zifangsky 13 | * @date 2019/6/4 14 | * @since 1.0.0 15 | */ 16 | public class TokenAccessContext extends DefaultAccessContext { 17 | private static final long serialVersionUID = 6748814370152609571L; 18 | 19 | /** 20 | * {@link SimpleAccessToken}的key 21 | */ 22 | public static final String SIMPLE_ACCESS_TOKEN_KEY = TokenAccessContext.class.getName() + ":simple_access_token"; 23 | 24 | /** 25 | * {@link SimpleRefreshToken}的key 26 | */ 27 | public static final String SIMPLE_REFRESH_TOKEN_KEY = TokenAccessContext.class.getName() + ":simple_refresh_token"; 28 | 29 | public SimpleAccessToken acquireAccessToken() { 30 | SimpleAccessToken accessToken = this.getSimpleAccessToken(); 31 | 32 | //如果为空,尝试从session获取 33 | if(accessToken == null){ 34 | Session session = this.acquireSession(); 35 | 36 | if(session != null){ 37 | accessToken = (SimpleAccessToken) session.getAttribute(TokenSessionContext.SIMPLE_ACCESS_TOKEN_KEY); 38 | } 39 | } 40 | 41 | return accessToken; 42 | } 43 | 44 | public SimpleRefreshToken acquireRefreshToken() { 45 | SimpleRefreshToken refreshToken = this.getSimpleRefreshToken(); 46 | 47 | //如果为空,尝试从session获取 48 | if(refreshToken == null){ 49 | Session session = this.acquireSession(); 50 | 51 | if(session != null){ 52 | refreshToken = (SimpleRefreshToken) session.getAttribute(TokenSessionContext.SIMPLE_REFRESH_TOKEN_KEY); 53 | } 54 | } 55 | 56 | return refreshToken; 57 | } 58 | 59 | public SimpleAccessToken getSimpleAccessToken() { 60 | return getByType(SIMPLE_ACCESS_TOKEN_KEY, SimpleAccessToken.class); 61 | } 62 | 63 | public void setSimpleAccessToken(SimpleAccessToken accessToken) { 64 | put(SIMPLE_ACCESS_TOKEN_KEY, accessToken); 65 | } 66 | 67 | public SimpleRefreshToken getSimpleRefreshToken() { 68 | return getByType(SIMPLE_REFRESH_TOKEN_KEY, SimpleRefreshToken.class); 69 | } 70 | 71 | public void setSimpleRefreshToken(SimpleRefreshToken refreshToken) { 72 | put(SIMPLE_REFRESH_TOKEN_KEY, refreshToken); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/cn/zifangsky/easylimit/CommonTest.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit; 2 | 3 | import cn.zifangsky.easylimit.session.impl.DefaultSessionContext; 4 | import cn.zifangsky.easylimit.utils.AntPathMatcher; 5 | import org.junit.jupiter.api.*; 6 | 7 | import java.util.Map; 8 | import java.util.concurrent.ConcurrentHashMap; 9 | 10 | /** 11 | * @author zifangsky 12 | * @date 2019/3/25 13 | * @since 1.0.0 14 | */ 15 | @DisplayName("基本测试") 16 | @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 17 | public class CommonTest { 18 | 19 | @Test 20 | @Order(1) 21 | @DisplayName("DefaultSessionContext") 22 | public void test1(){ 23 | String key = DefaultSessionContext.class.getName() + ":HOST"; 24 | 25 | Map map = new ConcurrentHashMap<>(); 26 | map.put(key, "127.0.0.1"); 27 | 28 | System.out.println(map.get(key)); 29 | } 30 | 31 | @Test 32 | @Order(2) 33 | @DisplayName("ConcurrentHashMap") 34 | public void test2(){ 35 | ConcurrentHashMap map = new ConcurrentHashMap<>(); 36 | map.put("112", "aaa"); 37 | map.put("223", "bbb"); 38 | map.remove("456"); 39 | System.out.println(map.size()); 40 | 41 | } 42 | 43 | @Test 44 | @Order(3) 45 | @DisplayName("ConcurrentHashMap") 46 | public void test3(){ 47 | ConcurrentHashMap> cacheMap = new ConcurrentHashMap<>(16); 48 | 49 | ConcurrentHashMap map = new ConcurrentHashMap<>(); 50 | map.put("112", "aaa"); 51 | map.put("223", "bbb"); 52 | cacheMap.put("session", map); 53 | 54 | ConcurrentHashMap tmp = cacheMap.get("session"); 55 | tmp.put("abc", "abc"); 56 | tmp.remove("223"); 57 | 58 | System.out.println(cacheMap.size()); 59 | } 60 | 61 | @Test 62 | @Order(4) 63 | @DisplayName("解析URL") 64 | public void test4(){ 65 | String redirectUrl = "http://example.com/test/aaa?id=5#point"; 66 | 67 | String anchor = null; 68 | int anchorIndex = redirectUrl.indexOf('#'); 69 | if (anchorIndex > -1) { 70 | anchor = redirectUrl.substring(anchorIndex); 71 | redirectUrl = redirectUrl.substring(0, anchorIndex); 72 | } 73 | 74 | System.out.println(redirectUrl); 75 | } 76 | 77 | @Test 78 | @Order(5) 79 | @DisplayName("匹配URL") 80 | public void test5(){ 81 | AntPathMatcher pathMatcher = new AntPathMatcher(); 82 | 83 | System.out.println(pathMatcher.match("/x/x/**/abc", "/x/x/x/abc")); 84 | System.out.println(pathMatcher.match("/css/**", "/css/a/b/c/")); 85 | System.out.println(pathMatcher.match("/**", "/a/b")); 86 | } 87 | 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/impl/DefaultLogoutFilter.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter.impl; 2 | 3 | import cn.zifangsky.easylimit.access.Access; 4 | import cn.zifangsky.easylimit.common.Constants; 5 | import cn.zifangsky.easylimit.enums.DefaultTokenRespEnums; 6 | import cn.zifangsky.easylimit.enums.ProjectModeEnums; 7 | import cn.zifangsky.easylimit.filter.AbstractAdviceFilter; 8 | import cn.zifangsky.easylimit.filter.impl.support.TokenRespMsg; 9 | import cn.zifangsky.easylimit.utils.SecurityUtils; 10 | import cn.zifangsky.easylimit.utils.WebUtils; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import javax.servlet.Filter; 15 | import javax.servlet.ServletRequest; 16 | import javax.servlet.ServletResponse; 17 | 18 | 19 | /** 20 | * 默认的注销{@link Filter} 21 | * 22 | * @author zifangsky 23 | * @date 2019/4/30 24 | * @since 1.0.0 25 | */ 26 | public class DefaultLogoutFilter extends AbstractAdviceFilter{ 27 | private static final Logger LOGGER = LoggerFactory.getLogger(DefaultLogoutFilter.class); 28 | 29 | /** 30 | * Token模式的未登录情况的错误返回 31 | */ 32 | private TokenRespMsg logoutRespMsg = new TokenRespMsg(DefaultTokenRespEnums.LOGOUT); 33 | 34 | /** 35 | * 注销之后的重定向URL 36 | */ 37 | private String logoutRedirectUrl = Constants.DEFAULT_LOGOUT_REDIRECT_URL; 38 | 39 | @Override 40 | protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { 41 | //1. 获取当前访问实例 42 | Access access = this.getAccess(request, response); 43 | 44 | //2.注销登录,清空相关缓存,停用session等 45 | try { 46 | access.logout(); 47 | }catch (Exception e){ 48 | LOGGER.error("Logout failed!", e); 49 | } 50 | 51 | //3. 重定向或返回提示信息 52 | if(ProjectModeEnums.DEFAULT.equals(this.getProjectMode()) && !WebUtils.isAjaxRequest(request)){ 53 | this.doRedirect(WebUtils.toHttp(request), WebUtils.toHttp(response), this.logoutRedirectUrl, null); 54 | }else{ 55 | this.generateTokenResponse(WebUtils.toHttp(response), this.logoutRespMsg); 56 | } 57 | 58 | return false; 59 | } 60 | 61 | /** 62 | * 获取访问实例 63 | */ 64 | protected Access getAccess(ServletRequest request, ServletResponse response) { 65 | return SecurityUtils.getAccess(); 66 | } 67 | 68 | public String getLogoutRedirectUrl() { 69 | return logoutRedirectUrl; 70 | } 71 | 72 | public void setLogoutRedirectUrl(String logoutRedirectUrl) { 73 | this.logoutRedirectUrl = logoutRedirectUrl; 74 | } 75 | 76 | public TokenRespMsg getLogoutRespMsg() { 77 | return logoutRespMsg; 78 | } 79 | 80 | public void setLogoutRespMsg(TokenRespMsg logoutRespMsg) { 81 | this.logoutRespMsg = logoutRespMsg; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/support/DefaultCacheTokenDAO.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl.support; 2 | 3 | import cn.zifangsky.easylimit.cache.Cache; 4 | import cn.zifangsky.easylimit.session.TokenDAO; 5 | import cn.zifangsky.easylimit.session.TokenOperateResolver; 6 | 7 | import java.util.concurrent.ConcurrentHashMap; 8 | 9 | /** 10 | * 默认实现的缓存{@link TokenDAO} 11 | * 12 | * @author zifangsky 13 | * @date 2019/6/24 14 | * @since 1.0.0 15 | */ 16 | public class DefaultCacheTokenDAO extends AbstractCacheTokenDAO{ 17 | 18 | /** 19 | * 使用{@link ConcurrentHashMap}存储所有{@link SimpleAccessToken} 20 | */ 21 | private ConcurrentHashMap accessTokenStorageMap; 22 | 23 | /** 24 | * 使用{@link ConcurrentHashMap}存储所有{@link SimpleRefreshToken} 25 | */ 26 | private ConcurrentHashMap refreshTokenStorageMap; 27 | 28 | public DefaultCacheTokenDAO(Cache cache) { 29 | super(cache); 30 | this.accessTokenStorageMap = new ConcurrentHashMap<>(); 31 | this.refreshTokenStorageMap = new ConcurrentHashMap<>(); 32 | } 33 | 34 | public DefaultCacheTokenDAO(Cache cache, TokenOperateResolver tokenOperateResolver) { 35 | super(cache, tokenOperateResolver); 36 | this.accessTokenStorageMap = new ConcurrentHashMap<>(); 37 | this.refreshTokenStorageMap = new ConcurrentHashMap<>(); 38 | } 39 | 40 | public DefaultCacheTokenDAO(Cache cache, String tokenCacheName, TokenOperateResolver tokenOperateResolver) { 41 | super(cache, tokenCacheName, tokenOperateResolver); 42 | this.accessTokenStorageMap = new ConcurrentHashMap<>(); 43 | this.refreshTokenStorageMap = new ConcurrentHashMap<>(); 44 | } 45 | 46 | @Override 47 | protected SimpleAccessToken doReadAccessToken(String accessToken) { 48 | return this.accessTokenStorageMap.get(accessToken); 49 | } 50 | 51 | @Override 52 | protected SimpleRefreshToken doReadRefreshToken(String refreshToken) { 53 | return this.refreshTokenStorageMap.get(refreshToken); 54 | } 55 | 56 | @Override 57 | protected void doUpdateAccessToken(SimpleAccessToken simpleAccessToken) { 58 | this.accessTokenStorageMap.put(simpleAccessToken.getAccessToken(), simpleAccessToken); 59 | } 60 | 61 | @Override 62 | protected void doUpdateRefreshToken(SimpleRefreshToken simpleRefreshToken) { 63 | this.refreshTokenStorageMap.put(simpleRefreshToken.getRefreshToken(), simpleRefreshToken); 64 | } 65 | 66 | @Override 67 | protected void doDeleteAccessToken(String accessToken) { 68 | this.accessTokenStorageMap.remove(accessToken); 69 | } 70 | 71 | @Override 72 | protected void doDeleteRefreshToken(String refreshToken) { 73 | this.refreshTokenStorageMap.remove(refreshToken); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/cache/Cache.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.cache; 2 | 3 | import cn.zifangsky.easylimit.exception.cache.CacheException; 4 | 5 | import java.util.Collection; 6 | import java.util.Map; 7 | import java.util.Set; 8 | 9 | /** 10 | * 缓存的抽象方法 11 | * 12 | * @author zifangsky 13 | * @date 2019/4/1 14 | * @since 1.0.0 15 | */ 16 | public interface Cache { 17 | /** 18 | * GET方法 19 | * 20 | * @param cacheName 键值对所属的缓存集合名称 21 | * @param key 通过KEY获取VALUE 22 | * @return V 23 | * @throws CacheException CacheException 24 | * @author zifangsky 25 | * @date 2019/4/1 17:21 26 | * @since 1.0.0 27 | */ 28 | V get(String cacheName, K key) throws CacheException; 29 | 30 | /** 31 | * PUT方法 32 | * 33 | * @param cacheName 键值对所属的缓存集合名称 34 | * @param key key 35 | * @param value value 36 | * @throws CacheException CacheException 37 | * @author zifangsky 38 | * @date 2019/4/1 17:21 39 | * @since 1.0.0 40 | */ 41 | void put(String cacheName, K key, V value) throws CacheException; 42 | 43 | /** 44 | * PUT ALL方法 45 | * 46 | * @param cacheName 键值对所属的缓存集合名称 47 | * @param sources 所有待保存的键值对 48 | * @throws CacheException CacheException 49 | * @author zifangsky 50 | * @date 2019/4/1 17:21 51 | * @since 1.0.0 52 | */ 53 | void putAll(String cacheName, Map sources) throws CacheException; 54 | 55 | /** 56 | * REMOVE方法 57 | * 58 | * @param cacheName 键值对所属的缓存集合名称 59 | * @param key key 60 | * @throws CacheException CacheException 61 | * @author zifangsky 62 | * @date 2019/4/1 17:21 63 | * @since 1.0.0 64 | */ 65 | void remove(String cacheName, K key) throws CacheException; 66 | 67 | /** 68 | * 清空缓存 69 | * 70 | * @param cacheName 键值对所属的缓存集合名称 71 | * @throws CacheException CacheException 72 | * @author zifangsky 73 | * @date 2019/4/1 17:24 74 | * @since 1.0.0 75 | */ 76 | void clear(String cacheName) throws CacheException; 77 | 78 | /** 79 | * 获取缓存的键值对的数量 80 | * 81 | * @param cacheName 键值对所属的缓存集合名称 82 | * @return int 83 | * @author zifangsky 84 | * @date 2019/4/1 17:25 85 | * @since 1.0.0 86 | */ 87 | int size(String cacheName); 88 | 89 | /** 90 | * 获取缓存的所有KEY 91 | * 92 | * @param cacheName 键值对所属的缓存集合名称 93 | * @return java.util.Set 94 | * @author zifangsky 95 | * @date 2019/4/1 17:25 96 | * @since 1.0.0 97 | */ 98 | Set keySet(String cacheName); 99 | 100 | /** 101 | * 获取缓存的所有VALUE 102 | * 103 | * @param cacheName 键值对所属的缓存集合名称 104 | * @return java.util.Collection 105 | * @author zifangsky 106 | * @date 2019/4/1 17:27 107 | * @since 1.0.0 108 | */ 109 | Collection values(String cacheName); 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/impl/support/AbstractProxiedFilter.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter.impl.support; 2 | 3 | import cn.zifangsky.easylimit.SecurityManager; 4 | import cn.zifangsky.easylimit.access.Access; 5 | import cn.zifangsky.easylimit.filter.AbstractOncePerRequestFilter; 6 | import cn.zifangsky.easylimit.filter.FilterChainResolver; 7 | 8 | import javax.servlet.FilterChain; 9 | import javax.servlet.ServletException; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | import java.io.IOException; 13 | 14 | /** 15 | * 所有自定义filter的执行入口 16 | * 17 | * @author zifangsky 18 | * @date 2019/5/15 19 | * @since 1.0.0 20 | */ 21 | public abstract class AbstractProxiedFilter extends AbstractOncePerRequestFilter{ 22 | /** 23 | * securityManager 24 | */ 25 | private SecurityManager securityManager; 26 | 27 | /** 28 | * filterChainManager 29 | */ 30 | private FilterChainResolver filterChainResolver; 31 | 32 | public AbstractProxiedFilter(SecurityManager securityManager, FilterChainResolver filterChainResolver) { 33 | if (securityManager == null){ 34 | throw new IllegalArgumentException("Parameter securityManager cannot be empty."); 35 | } 36 | if (filterChainResolver == null){ 37 | throw new IllegalArgumentException("Parameter filterChainResolver cannot be empty."); 38 | } 39 | 40 | this.securityManager = securityManager; 41 | this.filterChainResolver = filterChainResolver; 42 | } 43 | 44 | protected void executeFilterChain(HttpServletRequest request, HttpServletResponse response, FilterChain originalChain) throws ServletException, IOException { 45 | //1. 获取代理之后的FilterChain 46 | FilterChain filterChain = this.filterChainResolver.getProxiedFilterChain(request, response, originalChain); 47 | 48 | if(filterChain == null){ 49 | filterChain = originalChain; 50 | } 51 | 52 | //2. 执行过滤链 53 | filterChain.doFilter(request, response); 54 | } 55 | 56 | /** 57 | * 创建访问实例 58 | * @author zifangsky 59 | * @date 2019/5/16 13:46 60 | * @since 1.0.0 61 | * @param request HttpServletRequest 62 | * @param response HttpServletResponse 63 | * @return cn.zifangsky.easylimit.access.Access 64 | */ 65 | protected Access createAccess(HttpServletRequest request, HttpServletResponse response) throws Exception{ 66 | return new Access.Builder(this.securityManager, request, response).build(); 67 | } 68 | 69 | public SecurityManager getSecurityManager() { 70 | return securityManager; 71 | } 72 | 73 | public void setSecurityManager(SecurityManager securityManager) { 74 | this.securityManager = securityManager; 75 | } 76 | 77 | public FilterChainResolver getFilterChainResolver() { 78 | return filterChainResolver; 79 | } 80 | 81 | public void setFilterChainResolver(FilterChainResolver filterChainResolver) { 82 | this.filterChainResolver = filterChainResolver; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/impl/support/DefaultFilterEnums.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter.impl.support; 2 | 3 | import cn.zifangsky.easylimit.exception.filter.FilterException; 4 | import cn.zifangsky.easylimit.filter.AbstractFilter; 5 | import cn.zifangsky.easylimit.filter.impl.AnonymousFilter; 6 | import cn.zifangsky.easylimit.filter.impl.DefaultLoginFilter; 7 | import cn.zifangsky.easylimit.filter.impl.DefaultLogoutFilter; 8 | import cn.zifangsky.easylimit.filter.impl.PermissionsVerifyFilter; 9 | import cn.zifangsky.easylimit.filter.impl.RolesVerifyFilter; 10 | import cn.zifangsky.easylimit.utils.BeanUtils; 11 | 12 | import javax.servlet.Filter; 13 | import javax.servlet.FilterConfig; 14 | import javax.servlet.ServletException; 15 | import java.text.MessageFormat; 16 | import java.util.LinkedHashMap; 17 | import java.util.Map; 18 | 19 | /** 20 | * 默认的filter的枚举 21 | * 22 | * @author zifangsky 23 | * @date 2019/5/9 24 | * @since 1.0.0 25 | */ 26 | public enum DefaultFilterEnums { 27 | /** 28 | * 匿名访问的filter 29 | */ 30 | ANONYMOUS("anon", AnonymousFilter.class), 31 | /** 32 | * 需要登录的filter 33 | */ 34 | LOGIN("login", DefaultLoginFilter.class), 35 | /** 36 | * 注销的filter 37 | */ 38 | LOGOUT("logout", DefaultLogoutFilter.class), 39 | /** 40 | * 角色校验的filter 41 | */ 42 | ROLES("roles", RolesVerifyFilter.class), 43 | /** 44 | * 权限校验的filter 45 | */ 46 | PERMS("perms", PermissionsVerifyFilter.class) 47 | ; 48 | 49 | /** 50 | * filter的名称 51 | */ 52 | private String filterName; 53 | 54 | private Class filterClass; 55 | 56 | DefaultFilterEnums(String filterName, Class filterClass) { 57 | this.filterName = filterName; 58 | this.filterClass = filterClass; 59 | } 60 | 61 | /** 62 | * 实例化 63 | */ 64 | public Filter newInstance(){ 65 | return BeanUtils.newInstance(this.filterClass); 66 | } 67 | 68 | /** 69 | * 创建默认filter的实例 70 | */ 71 | public static Map createDefaultFilterMap(FilterConfig filterConfig){ 72 | Map result = new LinkedHashMap<>(values().length); 73 | 74 | for(DefaultFilterEnums defaultFilterEnum : values()){ 75 | Filter filter = defaultFilterEnum.newInstance(); 76 | 77 | if(filterConfig != null){ 78 | try { 79 | filter.init(filterConfig); 80 | } catch (ServletException e) { 81 | throw new FilterException(MessageFormat.format("Unable to correctly init default filter instance [{0}]", filter.getClass().getName())); 82 | } 83 | } 84 | 85 | result.put(defaultFilterEnum.getFilterName(), filter); 86 | } 87 | 88 | return result; 89 | } 90 | 91 | 92 | public String getFilterName() { 93 | return filterName; 94 | } 95 | 96 | public Class getFilterClass() { 97 | return filterClass; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/DefaultSessionContext.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl; 2 | 3 | import cn.zifangsky.easylimit.session.SessionContext; 4 | import cn.zifangsky.easylimit.utils.MapContext; 5 | 6 | import javax.servlet.ServletRequest; 7 | import javax.servlet.ServletResponse; 8 | import java.io.Serializable; 9 | import java.util.Map; 10 | 11 | /** 12 | * 默认的{@link SessionContext} 13 | * 14 | * @author zifangsky 15 | * @date 2019/3/25 16 | * @see SessionContext 17 | * @since 1.0.0 18 | */ 19 | public class DefaultSessionContext extends MapContext implements SessionContext { 20 | private static final long serialVersionUID = -4981375721508050679L; 21 | 22 | /** 23 | * host的key 24 | */ 25 | private static final String HOST_KEY = DefaultSessionContext.class.getName() + ":host"; 26 | 27 | /** 28 | * sessionId的key 29 | */ 30 | private static final String SESSION_ID_KEY = DefaultSessionContext.class.getName() + ":session_id"; 31 | 32 | /** 33 | * {@link ServletRequest}的key 34 | */ 35 | private static final String SERVLET_REQUEST_KEY = DefaultSessionContext.class.getName() + ":servlet_request"; 36 | 37 | /** 38 | * {@link ServletResponse}的key 39 | */ 40 | private static final String SERVLET_RESPONSE_KEY = DefaultSessionContext.class.getName() + ":servlet_response"; 41 | 42 | public DefaultSessionContext() { 43 | this(null, null); 44 | } 45 | 46 | public DefaultSessionContext(String host) { 47 | this(host, null); 48 | } 49 | 50 | public DefaultSessionContext(String host, Serializable sessionId) { 51 | super(); 52 | 53 | if(host != null){ 54 | this.setHost(host); 55 | } 56 | if(sessionId != null){ 57 | this.setSessionId(sessionId); 58 | } 59 | } 60 | 61 | public DefaultSessionContext(Map map) { 62 | super(map); 63 | } 64 | 65 | 66 | @Override 67 | public String getHost() { 68 | return getByType(HOST_KEY, String.class); 69 | } 70 | 71 | @Override 72 | public void setHost(String host) { 73 | put(HOST_KEY, host); 74 | } 75 | 76 | @Override 77 | public Serializable getSessionId() { 78 | return getByType(SESSION_ID_KEY, Serializable.class); 79 | } 80 | 81 | @Override 82 | public void setSessionId(Serializable sessionId) { 83 | put(SESSION_ID_KEY, sessionId); 84 | } 85 | 86 | @Override 87 | public ServletRequest getServletRequest() { 88 | return getByType(SERVLET_REQUEST_KEY, ServletRequest.class); 89 | } 90 | 91 | @Override 92 | public void setServletRequest(ServletRequest request) { 93 | put(SERVLET_REQUEST_KEY, request); 94 | } 95 | 96 | @Override 97 | public ServletResponse getServletResponse() { 98 | return getByType(SERVLET_RESPONSE_KEY, ServletResponse.class); 99 | } 100 | 101 | @Override 102 | public void setServletResponse(ServletResponse response) { 103 | put(SERVLET_RESPONSE_KEY, response); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/AbstractOncePerRequestFilter.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter; 2 | 3 | import cn.zifangsky.easylimit.filter.impl.support.TokenRespMsg; 4 | import cn.zifangsky.easylimit.utils.WebUtils; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | 7 | import javax.servlet.Filter; 8 | import javax.servlet.FilterChain; 9 | import javax.servlet.ServletException; 10 | import javax.servlet.ServletRequest; 11 | import javax.servlet.ServletResponse; 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | import java.io.IOException; 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | 18 | /** 19 | * {@link Filter}的基本实现 20 | * 21 | * @author zifangsky 22 | * @date 2019/4/29 23 | * @since 1.0.0 24 | */ 25 | public abstract class AbstractOncePerRequestFilter extends AbstractFilter{ 26 | /** 27 | * 真正的过滤逻辑 28 | * @author zifangsky 29 | * @date 2019/4/29 14:00 30 | * @since 1.0.0 31 | * @param request request 32 | * @param response response 33 | * @param filterChain 过滤链 34 | * @throws ServletException ServletException 35 | * @throws IOException IOException 36 | */ 37 | protected abstract void doFilterInternal( 38 | HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 39 | throws ServletException, IOException; 40 | 41 | @Override 42 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { 43 | if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { 44 | throw new ServletException("AbstractOncePerRequestFilter just supports HTTP requests"); 45 | } 46 | HttpServletRequest httpRequest = WebUtils.toHttp(request); 47 | HttpServletResponse httpResponse = WebUtils.toHttp(response); 48 | 49 | if (shouldNotFilter(httpRequest)) { 50 | //继续执行后面的过滤链 51 | filterChain.doFilter(request, response); 52 | } else { 53 | //执行当前filter的过滤逻辑 54 | doFilterInternal(httpRequest, httpResponse, filterChain); 55 | } 56 | } 57 | 58 | /** 59 | * 不执行过滤(主要用于被子类覆盖) 60 | * @author zifangsky 61 | * @date 2019/4/29 13:57 62 | * @since 1.0.0 63 | * @param request request 64 | * @return boolean 65 | */ 66 | protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { 67 | return false; 68 | } 69 | 70 | /** 71 | * 组装基于Token请求的返回 72 | */ 73 | protected void generateTokenResponse(HttpServletResponse response, TokenRespMsg tokenRespMsg) throws Exception { 74 | response.setCharacterEncoding("UTF-8"); 75 | response.setHeader("Content-type", "application/json;charset=UTF-8"); 76 | Map result = new HashMap<>(2); 77 | result.put("code", tokenRespMsg.getCode()); 78 | result.put("name", tokenRespMsg.getName()); 79 | result.put("msg", tokenRespMsg.getMsg()); 80 | 81 | ObjectMapper mapper = new ObjectMapper(); 82 | response.getWriter().write(mapper.writeValueAsString(result)); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/common/SpringContextUtils.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.common; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.beans.factory.BeanFactory; 5 | import org.springframework.context.ApplicationContext; 6 | import org.springframework.context.ApplicationContextAware; 7 | import org.springframework.context.ConfigurableApplicationContext; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.web.context.request.RequestContextHolder; 10 | import org.springframework.web.context.request.ServletRequestAttributes; 11 | 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | import javax.servlet.http.HttpSession; 15 | import java.util.Map; 16 | 17 | /** 18 | * 自定义Spring工具类 19 | * 20 | * @author zifangsky 21 | */ 22 | @Component("springContextUtils") 23 | public class SpringContextUtils implements ApplicationContextAware { 24 | private static ConfigurableApplicationContext applicationContext; 25 | 26 | /** 27 | * 获取ApplicationContext对象 28 | */ 29 | public static ApplicationContext getApplicationContext() { 30 | return applicationContext; 31 | } 32 | 33 | @Override 34 | public void setApplicationContext(ApplicationContext context) throws BeansException { 35 | applicationContext = (ConfigurableApplicationContext) context; 36 | } 37 | 38 | /** 39 | * 获取BeanFactory对象 40 | */ 41 | public static BeanFactory getBeanFactory() { 42 | return applicationContext.getBeanFactory(); 43 | } 44 | 45 | /** 46 | * 停止应用程序 47 | */ 48 | public static void close() { 49 | if (applicationContext != null) { 50 | applicationContext.close(); 51 | } 52 | } 53 | 54 | /** 55 | * 根据bean的名称获取bean 56 | */ 57 | public static Object getBeanByName(String name) { 58 | return applicationContext.getBean(name); 59 | } 60 | 61 | /** 62 | * 根据bean的class来查找对象 63 | */ 64 | public static T getBeanByClass(Class clazz) { 65 | return applicationContext.getBean(clazz); 66 | } 67 | 68 | /** 69 | * 根据bean的class来查找所有的对象(包括子类) 70 | */ 71 | public static Map getBeansByClass(Class c) { 72 | return applicationContext.getBeansOfType(c); 73 | } 74 | 75 | /** 76 | * 获取HttpServletRequest 77 | */ 78 | public static HttpServletRequest getRequest() { 79 | ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); 80 | return attributes.getRequest(); 81 | } 82 | 83 | /** 84 | * 获取HttpServletResponse 85 | */ 86 | public static HttpServletResponse getResponse() { 87 | ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); 88 | return attributes.getResponse(); 89 | } 90 | 91 | /** 92 | * 获取HttpSession 93 | */ 94 | public static HttpSession getSession() { 95 | ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); 96 | return attributes.getRequest().getSession(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/utils/MapContext.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.utils; 2 | 3 | import java.io.Serializable; 4 | import java.text.MessageFormat; 5 | import java.util.Collection; 6 | import java.util.Map; 7 | import java.util.Set; 8 | import java.util.concurrent.ConcurrentHashMap; 9 | 10 | /** 11 | * 装饰针对{@link java.util.Map}的操作,实际数据存储在内部的{@link ConcurrentHashMap}中 12 | * 13 | * @author zifangsky 14 | * @date 2019/3/25 15 | * @since 1.0.0 16 | */ 17 | public class MapContext implements Map, Serializable { 18 | private static final long serialVersionUID = 4888306869832322181L; 19 | 20 | /** 21 | * 真实存储数据的{@link java.util.Map} 22 | */ 23 | private final Map chunkMap; 24 | 25 | public MapContext() { 26 | this.chunkMap = new ConcurrentHashMap<>(); 27 | } 28 | 29 | public MapContext(Map map) { 30 | this(); 31 | this.putAll(map); 32 | } 33 | 34 | @Override 35 | public int size() { 36 | return this.chunkMap.size(); 37 | } 38 | 39 | @Override 40 | public boolean isEmpty() { 41 | return this.chunkMap.isEmpty(); 42 | } 43 | 44 | @Override 45 | public boolean containsKey(Object key) { 46 | return this.chunkMap.containsKey(key); 47 | } 48 | 49 | @Override 50 | public boolean containsValue(Object value) { 51 | return this.chunkMap.containsValue(value); 52 | } 53 | 54 | @Override 55 | public Object get(Object key) { 56 | return this.chunkMap.get(key); 57 | } 58 | 59 | public E getByType(String key, Class type) { 60 | Object result = this.get(key); 61 | 62 | if (result != null) { 63 | if (!type.isAssignableFrom(result.getClass())) { 64 | String msg = MessageFormat.format("Expected type was [{0}]," + 65 | "but the real key of object is [{1}].", type.getName(), result.getClass().getName()); 66 | throw new IllegalArgumentException(msg); 67 | } else { 68 | return (E) result; 69 | } 70 | } 71 | return null; 72 | } 73 | 74 | @Override 75 | public Object put(String key, Object value) { 76 | if (key != null && value != null) { 77 | return this.chunkMap.put(key, value); 78 | } 79 | 80 | return null; 81 | } 82 | 83 | @Override 84 | public Object remove(Object key) { 85 | return this.chunkMap.remove(key); 86 | } 87 | 88 | @Override 89 | public void putAll(Map map) { 90 | if (map != null && !map.isEmpty()) { 91 | this.chunkMap.putAll(map); 92 | } 93 | } 94 | 95 | @Override 96 | public void clear() { 97 | this.chunkMap.clear(); 98 | ; 99 | } 100 | 101 | @Override 102 | public Set keySet() { 103 | return this.chunkMap.keySet(); 104 | } 105 | 106 | @Override 107 | public Collection values() { 108 | return this.chunkMap.values(); 109 | } 110 | 111 | @Override 112 | public Set> entrySet() { 113 | return this.chunkMap.entrySet(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/utils/SnowFlake.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.utils; 2 | 3 | /** 4 | * 分布式ID自增算法——雪花算法(snowflake) 5 | * 6 | * @author zifangsky 7 | * @date 2019/4/2 8 | * @since 1.0.0 9 | */ 10 | public class SnowFlake { 11 | /** 12 | * 起始的时间戳(2019-01-01 00:00:00 000) 13 | */ 14 | private final static long START_STMP = 1546272000000L; 15 | 16 | /* ************ 每一部分占用的位数 *************** */ 17 | //序列号占用的位数 18 | private final static long SEQUENCE_BIT = 12; 19 | //机器标识占用的位数 20 | private final static long MACHINE_BIT = 5; 21 | //数据中心占用的位数 22 | private final static long DATACENTER_BIT = 5; 23 | 24 | /** 25 | * 每一部分的最大值 26 | */ 27 | private final static long MAX_DATACENTER_NUM = ~(-1L << DATACENTER_BIT); 28 | private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT); 29 | private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT); 30 | 31 | /** 32 | * 每一部分向左的位移 33 | */ 34 | private final static long MACHINE_LEFT = SEQUENCE_BIT; 35 | private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; 36 | private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT; 37 | 38 | //数据中心 39 | private long datacenterId; 40 | //机器标识 41 | private long machineId; 42 | //序列号 43 | private long sequence = 0L; 44 | //上一次时间戳 45 | private long lastStmp = -1L; 46 | 47 | public SnowFlake(long datacenterId, long machineId) { 48 | if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) { 49 | throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0"); 50 | } 51 | if (machineId > MAX_MACHINE_NUM || machineId < 0) { 52 | throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0"); 53 | } 54 | this.datacenterId = datacenterId; 55 | this.machineId = machineId; 56 | } 57 | 58 | /** 59 | * 产生下一个ID 60 | * 61 | * @return 62 | */ 63 | public synchronized long nextId() { 64 | long currStmp = getNewstmp(); 65 | if (currStmp < lastStmp) { 66 | throw new RuntimeException("Clock moved backwards. Refusing to generate id"); 67 | } 68 | 69 | if (currStmp == lastStmp) { 70 | //相同毫秒内,序列号自增 71 | sequence = (sequence + 1) & MAX_SEQUENCE; 72 | //同一毫秒的序列数已经达到最大 73 | if (sequence == 0L) { 74 | currStmp = getNextMill(); 75 | } 76 | } else { 77 | //不同毫秒内,序列号置为0 78 | sequence = 0L; 79 | } 80 | 81 | lastStmp = currStmp; 82 | 83 | return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分 84 | | datacenterId << DATACENTER_LEFT //数据中心部分 85 | | machineId << MACHINE_LEFT //机器标识部分 86 | | sequence; //序列号部分 87 | } 88 | 89 | private long getNextMill() { 90 | long mill = getNewstmp(); 91 | while (mill <= lastStmp) { 92 | mill = getNewstmp(); 93 | } 94 | return mill; 95 | } 96 | 97 | private long getNewstmp() { 98 | return System.currentTimeMillis(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/impl/PathFilterChainResolver.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter.impl; 2 | 3 | import cn.zifangsky.easylimit.filter.FilterChainManager; 4 | import cn.zifangsky.easylimit.filter.FilterChainResolver; 5 | import cn.zifangsky.easylimit.utils.AntPathMatcher; 6 | import cn.zifangsky.easylimit.utils.PatternMatcher; 7 | import cn.zifangsky.easylimit.utils.WebUtils; 8 | 9 | import javax.servlet.FilterChain; 10 | import javax.servlet.FilterConfig; 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | 14 | /** 15 | * 基于路径匹配场景获取代理{@link FilterChain} 16 | * 17 | * @author zifangsky 18 | * @date 2019/5/10 19 | * @since 1.0.0 20 | */ 21 | public class PathFilterChainResolver implements FilterChainResolver{ 22 | /** 23 | * 代理的{@link FilterChain}管理器 24 | */ 25 | private FilterChainManager filterChainManager; 26 | 27 | /** 28 | * 使用正则表达式匹配路径 29 | */ 30 | private PatternMatcher patternMatcher; 31 | 32 | public PathFilterChainResolver() { 33 | this.filterChainManager = new DefaultFilterChainManager(); 34 | this.patternMatcher = new AntPathMatcher(); 35 | } 36 | 37 | public PathFilterChainResolver(FilterConfig filterConfig) { 38 | this.filterChainManager = new DefaultFilterChainManager(filterConfig); 39 | this.patternMatcher = new AntPathMatcher(); 40 | } 41 | 42 | public PathFilterChainResolver(FilterChainManager filterChainManager) { 43 | this(filterChainManager, new AntPathMatcher()); 44 | } 45 | 46 | public PathFilterChainResolver(FilterChainManager filterChainManager, PatternMatcher patternMatcher) { 47 | this.filterChainManager = filterChainManager; 48 | this.patternMatcher = patternMatcher; 49 | } 50 | 51 | @Override 52 | public FilterChain getProxiedFilterChain(HttpServletRequest request, HttpServletResponse response, FilterChain originalChain) { 53 | //1. 遍历所有需要额外执行filter的拦截路径 54 | for(String patternPath : this.filterChainManager.getPatternPaths()){ 55 | //2. 判断是否和请求路径匹配 56 | if(this.matchPath(patternPath, request)){ 57 | //3. 获取代理后的FilterChain 58 | return this.filterChainManager.getProxiedFilterChain(originalChain, patternPath); 59 | } 60 | } 61 | 62 | return null; 63 | } 64 | 65 | 66 | /** 67 | * 校验请求URI和预设的正则类型的拦截路径是否匹配 68 | */ 69 | protected boolean matchPath(String patternPath, HttpServletRequest httpServletRequest){ 70 | //获取不包含当前域名和项目名的URI 71 | String servletPath = WebUtils.getServletPath(httpServletRequest); 72 | return this.matchPath(patternPath, servletPath); 73 | } 74 | 75 | /** 76 | * 校验请求URI和预设的正则类型的拦截路径是否匹配 77 | */ 78 | protected boolean matchPath(String patternPath, String servletPath){ 79 | return patternMatcher.match(patternPath, servletPath); 80 | } 81 | 82 | public FilterChainManager getFilterChainManager() { 83 | return filterChainManager; 84 | } 85 | 86 | public void setFilterChainManager(FilterChainManager filterChainManager) { 87 | this.filterChainManager = filterChainManager; 88 | } 89 | 90 | public PatternMatcher getPatternMatcher() { 91 | return patternMatcher; 92 | } 93 | 94 | public void setPatternMatcher(PatternMatcher patternMatcher) { 95 | this.patternMatcher = patternMatcher; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/enums/EncryptionTypeEnums.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.enums; 2 | 3 | import cn.zifangsky.easylimit.utils.EncryptUtils; 4 | 5 | /** 6 | * 加密方式 7 | * 8 | * @author zifangsky 9 | * @date 2017/11/2 10 | * @since 1.0.0 11 | */ 12 | public enum EncryptionTypeEnums { 13 | /** 14 | * 不加密 15 | */ 16 | None("None", "No Encryption"), 17 | /** 18 | * 自定义加密 19 | */ 20 | CUSTOM("Custom", "Custom Encryption"), 21 | /** 22 | * Base64加密 23 | */ 24 | Base64("Base64", "Base64 Encryption"), 25 | /** 26 | * Md5Hex加密 27 | */ 28 | Md5Hex("Md5Hex", "Md5Hex Encryption"), 29 | /** 30 | * Sha1Hex加密 31 | */ 32 | Sha1Hex("Sha1Hex", "Sha1Hex Encryption"), 33 | /** 34 | * Sha256Hex加密 35 | */ 36 | Sha256Hex("Sha256Hex", "Sha256Hex Encryption"), 37 | /** 38 | * Sha512Hex加密 39 | */ 40 | Sha512Hex("Sha512Hex", "Sha512Hex Encryption"), 41 | /** 42 | * Md5Crypt加密 43 | */ 44 | Md5Crypt("Md5Crypt", "Md5Crypt Encryption"), 45 | /** 46 | * Sha256Crypt加密 47 | */ 48 | Sha256Crypt("Sha256Crypt", "Sha256Crypt Encryption"), 49 | /** 50 | * Sha512Crypt加密 51 | */ 52 | Sha512Crypt("Sha512Crypt", "Sha512Crypt Encryption"); 53 | 54 | /** 55 | * CODE 56 | */ 57 | private String code; 58 | /** 59 | * 描述 60 | */ 61 | private String description; 62 | 63 | EncryptionTypeEnums(String code, String description) { 64 | this.code = code; 65 | this.description = description; 66 | } 67 | 68 | /** 69 | * 校验密码是否匹配 70 | * 71 | * @param correctPassword 数据库中正确的密码 72 | * @param validatedPassword 来至外部待验证的密码 73 | * @return boolean 74 | * @author zifangsky 75 | * @date 2019/4/3 14:20 76 | * @since 1.0.0 77 | */ 78 | public static boolean verifyCredentials(EncryptionTypeEnums type, String correctPassword, String validatedPassword) { 79 | if (correctPassword == null || validatedPassword == null) { 80 | throw new IllegalArgumentException("Parameter correctPassword and validatedPassword cannot be empty."); 81 | } 82 | 83 | switch (type) { 84 | case None: 85 | return correctPassword.equals(validatedPassword); 86 | case Base64: 87 | return correctPassword.equals(EncryptUtils.base64Encode(validatedPassword)); 88 | case Md5Hex: 89 | return correctPassword.equals(EncryptUtils.md5Hex(validatedPassword)); 90 | case Sha1Hex: 91 | return correctPassword.equals(EncryptUtils.sha1Hex(validatedPassword)); 92 | case Sha256Hex: 93 | return correctPassword.equals(EncryptUtils.sha256Hex(validatedPassword)); 94 | case Sha512Hex: 95 | return correctPassword.equals(EncryptUtils.sha512Hex(validatedPassword)); 96 | case Md5Crypt: 97 | return EncryptUtils.checkMd5Crypt(validatedPassword, correctPassword); 98 | case Sha256Crypt: 99 | return EncryptUtils.checkSha256Crypt(validatedPassword, correctPassword); 100 | case Sha512Crypt: 101 | return EncryptUtils.checkSha512Crypt(validatedPassword, correctPassword); 102 | default: 103 | return false; 104 | } 105 | } 106 | 107 | public String getCode() { 108 | return code; 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/permission/aop/AbstractAnnotationResolver.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.permission.aop; 2 | 3 | import cn.zifangsky.easylimit.access.Access; 4 | import cn.zifangsky.easylimit.exception.authc.AuthenticationException; 5 | import cn.zifangsky.easylimit.utils.SecurityUtils; 6 | import org.aopalliance.intercept.MethodInvocation; 7 | import org.springframework.core.annotation.AnnotationUtils; 8 | import org.springframework.util.ClassUtils; 9 | 10 | import java.lang.annotation.Annotation; 11 | import java.lang.reflect.Method; 12 | 13 | /** 14 | * 注解相关AOP处理器 15 | * 16 | * @author zifangsky 17 | * @date 2019/6/18 18 | * @since 1.0.0 19 | */ 20 | public abstract class AbstractAnnotationResolver { 21 | /** 22 | * 注解类型 23 | */ 24 | protected Class annotationClass; 25 | 26 | public AbstractAnnotationResolver(Class annotationClass) { 27 | if(annotationClass == null){ 28 | throw new IllegalArgumentException("Parameter annotationClass cannot be empty."); 29 | } 30 | 31 | this.annotationClass = annotationClass; 32 | } 33 | 34 | /** 35 | * 校验角色、权限 36 | * @author zifangsky 37 | * @date 2019/6/19 10:26 38 | * @since 1.0.0 39 | * @param annotation 权限注解 40 | */ 41 | protected abstract void assertPermission(Annotation annotation) throws AuthenticationException; 42 | 43 | /** 44 | * 校验角色、权限 45 | * @author zifangsky 46 | * @date 2019/6/19 10:26 47 | * @since 1.0.0 48 | * @param invocation MethodInvocation 49 | */ 50 | public void assertPermission(MethodInvocation invocation) throws AuthenticationException{ 51 | this.assertPermission(this.getAnnotation(invocation)); 52 | } 53 | 54 | /** 55 | * 返回当前处理器是否支持 56 | * @author zifangsky 57 | * @date 2019/6/19 11:34 58 | * @since 1.0.0 59 | * @param invocation MethodInvocation 60 | * @return boolean 61 | */ 62 | public boolean support(MethodInvocation invocation){ 63 | return this.getAnnotation(invocation) != null; 64 | } 65 | 66 | /** 67 | * 获取注解数据 68 | */ 69 | protected Annotation getAnnotation(MethodInvocation invocation) { 70 | return this.getAnnotation(invocation, annotationClass); 71 | } 72 | 73 | /** 74 | * 获取注解数据 75 | */ 76 | protected Annotation getAnnotation(MethodInvocation invocation, Class clazz) { 77 | Method method = invocation.getMethod(); 78 | 79 | //1. 查找注解 80 | Annotation annotation = AnnotationUtils.findAnnotation(method, clazz); 81 | if (annotation != null) { 82 | return annotation; 83 | } 84 | 85 | //2. 尝试从实现类中查找注解 86 | Class targetClass = invocation.getThis().getClass(); 87 | method = ClassUtils.getMostSpecificMethod(method, targetClass); 88 | annotation = AnnotationUtils.findAnnotation(method, clazz); 89 | 90 | if (annotation != null) { 91 | return annotation; 92 | } 93 | 94 | return AnnotationUtils.findAnnotation(invocation.getThis().getClass(), clazz); 95 | } 96 | 97 | /** 98 | * 获取请求实例 99 | */ 100 | protected Access getAccess() { 101 | return SecurityUtils.getAccess(); 102 | } 103 | 104 | public Class getAnnotationClass() { 105 | return annotationClass; 106 | } 107 | 108 | public void setAnnotationClass(Class annotationClass) { 109 | this.annotationClass = annotationClass; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/utils/BeanUtils.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.utils; 2 | 3 | import cn.zifangsky.easylimit.exception.EasyLimitException; 4 | import org.apache.commons.beanutils.BeanUtilsBean; 5 | import org.apache.commons.lang3.reflect.FieldUtils; 6 | 7 | import java.lang.reflect.Field; 8 | import java.text.MessageFormat; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | /** 13 | * Java Bean相关公共方法 14 | * 15 | * @author zifangsky 16 | * @date 2017/11/14 17 | * @since 1.0.0 18 | */ 19 | public class BeanUtils { 20 | 21 | public static E newInstance(Class clazz) { 22 | if (clazz == null) { 23 | throw new IllegalArgumentException("Parameter clazz cannot be empty."); 24 | } else { 25 | try { 26 | return clazz.newInstance(); 27 | } catch (Exception e) { 28 | throw new EasyLimitException(MessageFormat.format("Unable to instantiate class [{0}].", clazz.getName())); 29 | } 30 | } 31 | } 32 | 33 | /** 34 | * 复制Bean的参数 35 | * 36 | * @param source source 37 | * @param target target 38 | * @author zifangsky 39 | * @date 2017/11/14 13:44 40 | * @since 1.0.0 41 | */ 42 | public static void copyProperties(final Object source, final Object target) throws Exception { 43 | BeanUtilsBean.getInstance().copyProperties(target, source); 44 | } 45 | 46 | /** 47 | * 通过参数名获取参数值 48 | * 49 | * @param bean Bean 50 | * @param name 参数名 51 | * @return java.lang.Object 52 | * @author zifangsky 53 | * @date 2017/11/14 13:58 54 | * @since 1.0.0 55 | */ 56 | public static Object getProperty(final Object bean, final String name) throws Exception { 57 | return BeanUtilsBean.getInstance().getPropertyUtils().getNestedProperty(bean, name); 58 | } 59 | 60 | /** 61 | * 通过参数名获取参数值 62 | * 63 | * @param bean Bean 64 | * @param name 参数名 65 | * @return java.lang.Object 66 | * @author zifangsky 67 | * @date 2017/11/14 13:58 68 | * @since 1.0.0 69 | */ 70 | public static Object getSimpleProperty(final Object bean, final String name) throws Exception { 71 | return BeanUtilsBean.getInstance().getPropertyUtils().getSimpleProperty(bean, name); 72 | 73 | } 74 | 75 | /** 76 | * 将Map转Bean 77 | * 78 | * @param clazz Bean的类型 79 | * @param properties 所有参数 80 | * @return K 81 | * @author zifangsky 82 | * @date 2017/11/14 14:03 83 | * @since 1.0.0 84 | */ 85 | public static K mapToObject(Class clazz, final Map properties) throws Exception { 86 | if (properties == null) { 87 | return null; 88 | } else { 89 | K k = clazz.newInstance(); 90 | org.apache.commons.beanutils.BeanUtils.populate(k, properties); 91 | 92 | return k; 93 | } 94 | } 95 | 96 | /** 97 | * 将Bean的所有参数转换为Map 98 | * 99 | * @param bean Bean 100 | * @return java.util.Map 101 | * @author zifangsky 102 | * @date 2017/11/14 14:21 103 | * @since 1.0.0 104 | */ 105 | public static Map objectToMap(Object bean) throws Exception { 106 | if (bean == null) { 107 | return null; 108 | } else { 109 | Field[] allFields = FieldUtils.getAllFields(bean.getClass()); 110 | Map map = new HashMap<>(allFields.length); 111 | 112 | for (Field field : allFields) { 113 | field.setAccessible(true); 114 | map.put(field.getName(), field.get(bean)); 115 | } 116 | 117 | return map; 118 | } 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/support/CookieInfo.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl.support; 2 | 3 | import cn.zifangsky.easylimit.utils.CookieUtils; 4 | import org.apache.commons.lang3.StringUtils; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | 8 | /** 9 | * cookie基本信息 10 | * 11 | * @author zifangsky 12 | * @date 2019/4/12 13 | * @since 1.0.0 14 | */ 15 | public class CookieInfo { 16 | /** 17 | * Root路径 18 | */ 19 | public static final String ROOT_PATH = "/"; 20 | 21 | /** 22 | * cookie名称 23 | */ 24 | private String name; 25 | /** 26 | * cookie值 27 | */ 28 | private String value; 29 | /** 30 | * cookie所属的子域 31 | */ 32 | private String domain; 33 | /** 34 | * 设置cookie路径 35 | */ 36 | private String path; 37 | /** 38 | * 设置cookie的最大生存期 39 | */ 40 | private int maxAge; 41 | /** 42 | * 是否只允许HTTPS访问 43 | */ 44 | private boolean secure; 45 | /** 46 | * 是否将cookie设置成HttpOnly 47 | */ 48 | private boolean httpOnly; 49 | 50 | public CookieInfo(String name) { 51 | this(name, true); 52 | } 53 | 54 | public CookieInfo(String name, boolean httpOnly) { 55 | this(name, null, CookieUtils.COOKIE_MAX_AGE, false, httpOnly); 56 | } 57 | 58 | public CookieInfo(String name, String domain, int maxAge, boolean secure, boolean httpOnly) { 59 | this(name, null, domain, null, maxAge, secure, httpOnly); 60 | } 61 | 62 | public CookieInfo(String name, String value, String domain, String path, int maxAge, boolean secure, boolean httpOnly) { 63 | this.name = name; 64 | this.value = value; 65 | this.domain = domain; 66 | this.path = path; 67 | this.maxAge = maxAge; 68 | this.secure = secure; 69 | this.httpOnly = httpOnly; 70 | } 71 | 72 | /** 73 | * 自动计算Path 74 | */ 75 | public String calculatePath(HttpServletRequest request) { 76 | String path = this.path; 77 | if (StringUtils.isBlank(path)) { 78 | path = StringUtils.trimToNull(request.getContextPath()); 79 | } 80 | 81 | if (path == null) { 82 | path = ROOT_PATH; 83 | } 84 | 85 | return path; 86 | } 87 | 88 | public String getName() { 89 | return name; 90 | } 91 | 92 | public void setName(String name) { 93 | this.name = name; 94 | } 95 | 96 | public String getValue() { 97 | return value; 98 | } 99 | 100 | public void setValue(String value) { 101 | this.value = value; 102 | } 103 | 104 | public String getDomain() { 105 | return domain; 106 | } 107 | 108 | public void setDomain(String domain) { 109 | this.domain = domain; 110 | } 111 | 112 | public String getPath() { 113 | return path; 114 | } 115 | 116 | public void setPath(String path) { 117 | this.path = path; 118 | } 119 | 120 | public int getMaxAge() { 121 | return maxAge; 122 | } 123 | 124 | public void setMaxAge(int maxAge) { 125 | this.maxAge = maxAge; 126 | } 127 | 128 | public boolean isSecure() { 129 | return secure; 130 | } 131 | 132 | public void setSecure(boolean secure) { 133 | this.secure = secure; 134 | } 135 | 136 | public boolean isHttpOnly() { 137 | return httpOnly; 138 | } 139 | 140 | public void setHttpOnly(boolean httpOnly) { 141 | this.httpOnly = httpOnly; 142 | } 143 | 144 | @Override 145 | public String toString() { 146 | return "CookieInfo{" + 147 | "name='" + name + '\'' + 148 | ", value='" + value + '\'' + 149 | ", domain='" + domain + '\'' + 150 | ", path='" + path + '\'' + 151 | ", maxAge=" + maxAge + 152 | ", secure=" + secure + 153 | ", httpOnly=" + httpOnly + 154 | '}'; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/DefaultTokenOperateResolver.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl; 2 | 3 | import cn.zifangsky.easylimit.authc.PrincipalInfo; 4 | import cn.zifangsky.easylimit.authc.ValidatedInfo; 5 | import cn.zifangsky.easylimit.session.TokenOperateResolver; 6 | import cn.zifangsky.easylimit.session.impl.support.SimpleAccessToken; 7 | import cn.zifangsky.easylimit.session.impl.support.SimpleRefreshToken; 8 | import cn.zifangsky.easylimit.session.impl.support.TokenInfo; 9 | import cn.zifangsky.easylimit.utils.DateUtils; 10 | import cn.zifangsky.easylimit.utils.EncryptUtils; 11 | 12 | import java.io.Serializable; 13 | import java.time.Duration; 14 | import java.time.LocalDateTime; 15 | import java.time.temporal.ChronoUnit; 16 | 17 | /** 18 | * 默认实现 19 | * 20 | * @author zifangsky 21 | * @date 2019/6/4 22 | * @since 1.0.0 23 | */ 24 | public class DefaultTokenOperateResolver implements TokenOperateResolver{ 25 | 26 | @Override 27 | public SimpleAccessToken createAccessToken(PrincipalInfo principalInfo, TokenInfo tokenInfo, Serializable sessionId) { 28 | String account = principalInfo.getAccount(); 29 | LocalDateTime now = DateUtils.now(); 30 | //过期时间 31 | LocalDateTime expirationTime = now.plus(tokenInfo.getAccessTokenTimeout(), tokenInfo.getAccessTokenTimeoutUnit()); 32 | //剩余秒数 33 | long expiresIn = Duration.between(now, expirationTime).getSeconds(); 34 | 35 | //1. 拼装待加密字符串(account + 当前精确到毫秒的时间戳) 36 | String str = account + String.valueOf(DateUtils.nowMilliSecondTimestamp(null)); 37 | 38 | //2. SHA1加密 39 | String accessTokenStr = "1." + EncryptUtils.sha1Hex(str) + "." + expiresIn + "." + DateUtils.getSecondTimestamp(expirationTime, null); 40 | 41 | //3. 返回对象 42 | return new SimpleAccessToken(accessTokenStr, expiresIn, principalInfo, sessionId, now); 43 | } 44 | 45 | @Override 46 | public SimpleRefreshToken createRefreshToken(ValidatedInfo validatedInfo, TokenInfo tokenInfo, String accessToken) { 47 | String account = validatedInfo.getSubject(); 48 | LocalDateTime now = DateUtils.now(); 49 | //过期时间 50 | LocalDateTime expirationTime = now.plus(tokenInfo.getRefreshTokenTimeout(), tokenInfo.getRefreshTokenTimeoutUnit()); 51 | //剩余秒数 52 | long expiresIn = Duration.between(now, expirationTime).getSeconds(); 53 | 54 | //1. 拼装待加密字符串(account + accessToken +当前精确到毫秒的时间戳) 55 | String str = account + accessToken + String.valueOf(DateUtils.nowMilliSecondTimestamp(null)); 56 | 57 | //2. SHA1加密 58 | String refreshTokenStr = "2." + EncryptUtils.sha1Hex(str) + "." + expiresIn + "." + DateUtils.getSecondTimestamp(expirationTime, null); 59 | 60 | //3. 返回对象 61 | return new SimpleRefreshToken(refreshTokenStr, expiresIn, accessToken, validatedInfo, now); 62 | } 63 | 64 | @Override 65 | public boolean isValid(SimpleAccessToken simpleAccessToken) { 66 | if(simpleAccessToken == null){ 67 | return false; 68 | }else{ 69 | //已经过期 70 | if(simpleAccessToken.isExpired()){ 71 | return false; 72 | } 73 | 74 | LocalDateTime now = DateUtils.now(); 75 | //过期时间 76 | LocalDateTime expirationTime = simpleAccessToken.getCreateTime().plus(simpleAccessToken.getExpiresIn(), ChronoUnit.SECONDS); 77 | 78 | return now.isBefore(expirationTime); 79 | } 80 | } 81 | 82 | @Override 83 | public boolean isValid(SimpleRefreshToken simpleRefreshToken) { 84 | if(simpleRefreshToken == null){ 85 | return false; 86 | }else{ 87 | //已经过期 88 | if(simpleRefreshToken.isExpired()){ 89 | return false; 90 | } 91 | 92 | LocalDateTime now = DateUtils.now(); 93 | //过期时间 94 | LocalDateTime expirationTime = simpleRefreshToken.getCreateTime().plus(simpleRefreshToken.getExpiresIn(), ChronoUnit.SECONDS); 95 | 96 | return now.isBefore(expirationTime); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/FilterChainManager.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter; 2 | 3 | import javax.servlet.Filter; 4 | import javax.servlet.FilterChain; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Set; 8 | 9 | /** 10 | * 代理的{@link FilterChain}管理器 11 | * 12 | * @author zifangsky 13 | * @date 2019/5/9 14 | * @since 1.0.0 15 | */ 16 | public interface FilterChainManager { 17 | 18 | /** 19 | * 根据filter的名称获取filter 20 | * @author zifangsky 21 | * @date 2019/5/9 16:03 22 | * @since 1.0.0 23 | * @param filterName filter的名称 24 | * @return javax.servlet.Filter 25 | */ 26 | Filter getFilter(String filterName); 27 | 28 | /** 29 | * 获取所有可用的filter 30 | * @author zifangsky 31 | * @date 2019/5/9 16:03 32 | * @since 1.0.0 33 | * @return java.util.Map 34 | */ 35 | Map getAvailableFilters(); 36 | 37 | /** 38 | * 获取某个特定拦截路径需要执行的filter 39 | * @author zifangsky 40 | * @date 2019/5/9 16:06 41 | * @since 1.0.0 42 | * @param patternPath 某个特定拦截路径 43 | * @return java.util.List 44 | */ 45 | List getPatternPathFilters(String patternPath); 46 | 47 | /** 48 | * 获取所有的特定的拦截路径 49 | * @author zifangsky 50 | * @date 2019/5/9 16:37 51 | * @since 1.0.0 52 | * @return java.util.Set 53 | */ 54 | Set getPatternPaths(); 55 | 56 | /** 57 | * 是否存在FilterChain 58 | * @author zifangsky 59 | * @date 2019/5/9 16:39 60 | * @since 1.0.0 61 | * @return boolean 62 | */ 63 | boolean hasFilterChain(); 64 | 65 | /** 66 | * 获取代理之后的FilterChain 67 | * @author zifangsky 68 | * @date 2019/5/9 16:42 69 | * @since 1.0.0 70 | * @param original 原过滤链 71 | * @param patternPath 某个特定拦截路径 72 | * @return javax.servlet.FilterChain 73 | */ 74 | FilterChain getProxiedFilterChain(FilterChain original, String patternPath); 75 | 76 | /** 77 | * 添加filter 78 | * @author zifangsky 79 | * @date 2019/5/9 16:50 80 | * @since 1.0.0 81 | * @param filterName filter的名称 82 | * @param filter filter 83 | */ 84 | void addFilter(String filterName, Filter filter); 85 | 86 | /** 87 | * 添加filter 88 | * @author zifangsky 89 | * @date 2019/5/9 16:50 90 | * @since 1.0.0 91 | * @param filterName filter的名称 92 | * @param filter filter 93 | * @param init 是否使用FilterConfig初始化 94 | */ 95 | void addFilter(String filterName, Filter filter, boolean init); 96 | 97 | /** 98 | * 添加filter 99 | * @author zifangsky 100 | * @date 2019/5/9 16:50 101 | * @since 1.0.0 102 | * @param filterName filter的名称 103 | * @param filter filter 104 | * @param init 是否使用FilterConfig初始化 105 | * @param overwrite 是否覆盖前面的配置 106 | */ 107 | void addFilter(String filterName, Filter filter, boolean init, boolean overwrite); 108 | 109 | /** 110 | * 创建某个特定拦截路径的专有FilterChain 111 | * @author zifangsky 112 | * @date 2019/5/9 17:04 113 | * @since 1.0.0 114 | * @param patternPath 某个特定拦截路径,如:/aaa/bbb/** 115 | * @param filterExpressionArr filter表达式数组,如:login, roles[reviewer, subscriber], perms[list, edit] 116 | */ 117 | void createFilterChain(String patternPath, String...filterExpressionArr); 118 | 119 | /** 120 | * 创建某个特定拦截路径的专有FilterChain 121 | * @author zifangsky 122 | * @date 2019/5/9 17:04 123 | * @since 1.0.0 124 | * @param patternPath 某个特定拦截路径 125 | * @param filterName filter的名称 126 | */ 127 | void addToFilterChain(String patternPath, String filterName); 128 | 129 | /** 130 | * 创建某个特定拦截路径的专有FilterChain 131 | * @author zifangsky 132 | * @date 2019/5/9 17:04 133 | * @since 1.0.0 134 | * @param patternPath 某个特定拦截路径 135 | * @param filterName filter的名称 136 | * @param controlVal 多个具体的角色或权限要求,比如:reviewer, subscriber 137 | */ 138 | void addToFilterChain(String patternPath, String filterName, String[] controlVal); 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/DefaultSessionValidationScheduled.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl; 2 | 3 | import cn.zifangsky.easylimit.common.Constants; 4 | import cn.zifangsky.easylimit.enums.DefaultTimeEnums; 5 | import cn.zifangsky.easylimit.session.Session; 6 | import cn.zifangsky.easylimit.session.SessionValidationScheduled; 7 | import cn.zifangsky.easylimit.utils.DateUtils; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.text.MessageFormat; 12 | import java.util.concurrent.ScheduledExecutorService; 13 | import java.util.concurrent.ScheduledThreadPoolExecutor; 14 | import java.util.concurrent.ThreadFactory; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | /** 18 | * 默认的{@link SessionValidationScheduled} 19 | * 20 | * @author zifangsky 21 | * @date 2019/4/1 22 | * @since 1.0.0 23 | */ 24 | public class DefaultSessionValidationScheduled implements SessionValidationScheduled, Runnable { 25 | private static final Logger LOGGER = LoggerFactory.getLogger(DefaultSessionValidationScheduled.class); 26 | 27 | /** 28 | * 调用{@link AbstractValidationSessionManager#validateSessions()}方法执行最终的{@link Session}校验逻辑 29 | */ 30 | private AbstractValidationSessionManager validationSessionManager; 31 | /** 32 | * 定时任务 33 | */ 34 | private ScheduledExecutorService scheduledExecutorService; 35 | /** 36 | * 定时任务是否已经开始 37 | */ 38 | private boolean enabled = false; 39 | 40 | /** 41 | * {@link Session}校验的时间间隔 42 | */ 43 | private long sessionValidationInterval = DefaultTimeEnums.SESSION_VALIDATION.getTime(); 44 | /** 45 | * {@link Session}校验的时间单位 46 | */ 47 | private TimeUnit sessionValidationUnit = DefaultTimeEnums.SESSION_VALIDATION.getTimeUnit(); 48 | 49 | 50 | public DefaultSessionValidationScheduled() { 51 | } 52 | 53 | public DefaultSessionValidationScheduled(AbstractValidationSessionManager validationSessionManager) { 54 | this.validationSessionManager = validationSessionManager; 55 | } 56 | 57 | public DefaultSessionValidationScheduled(AbstractValidationSessionManager validationSessionManager, long sessionValidationInterval, TimeUnit sessionValidationUnit) { 58 | this.validationSessionManager = validationSessionManager; 59 | this.sessionValidationInterval = sessionValidationInterval; 60 | this.sessionValidationUnit = sessionValidationUnit; 61 | } 62 | 63 | @Override 64 | public boolean isEnabled() { 65 | return this.enabled; 66 | } 67 | 68 | @Override 69 | public void startScheduled() { 70 | if (scheduledExecutorService == null) { 71 | //1. 定义一个1个线程的定时任务 72 | scheduledExecutorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() { 73 | @Override 74 | public Thread newThread(Runnable r) { 75 | Thread thread = new Thread(r); 76 | thread.setDaemon(true); 77 | thread.setName(Constants.SESSION_CHECK_THREAD_NAME); 78 | 79 | return thread; 80 | } 81 | }); 82 | 83 | //2. 设置执行频率 84 | scheduledExecutorService.scheduleAtFixedRate(this, 85 | sessionValidationInterval, 86 | sessionValidationInterval, 87 | sessionValidationUnit); 88 | 89 | //3. 标识任务已经开始 90 | this.enabled = true; 91 | } 92 | } 93 | 94 | @Override 95 | public void stopScheduled() { 96 | if (scheduledExecutorService != null) { 97 | this.scheduledExecutorService.shutdown(); 98 | } 99 | this.enabled = false; 100 | } 101 | 102 | /** 103 | * 定时任务的执行逻辑 104 | * 105 | * @author zifangsky 106 | * @date 2019/4/1 13:35 107 | * @since 1.0.0 108 | */ 109 | @Override 110 | public void run() { 111 | LOGGER.debug(MessageFormat.format("Start the session validation task at:[{0}].", DateUtils.nowStr())); 112 | 113 | this.validationSessionManager.performValidationTask(); 114 | 115 | LOGGER.debug(MessageFormat.format("End the session validation task at:[{0}].", DateUtils.nowStr())); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/cache/impl/DefaultRedisCache.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.cache.impl; 2 | 3 | import cn.zifangsky.easylimit.cache.Cache; 4 | import cn.zifangsky.easylimit.exception.cache.CacheException; 5 | import org.springframework.data.redis.core.HashOperations; 6 | import org.springframework.data.redis.core.RedisTemplate; 7 | 8 | import java.io.Serializable; 9 | import java.util.Collection; 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | /** 14 | * 默认的Redis缓存方式 15 | * 16 | * @author zifangsky 17 | * @date 2019/4/11 18 | * @since 1.0.0 19 | */ 20 | public class DefaultRedisCache implements Cache { 21 | /** 22 | * 使用{@link RedisTemplate}操作所有{@link Cache} 23 | */ 24 | private RedisTemplate redisTemplate; 25 | /** 26 | * 使用{@link RedisTemplate}的HASH存储具体的{@link Cache} 27 | */ 28 | private HashOperations opsForHash; 29 | 30 | public DefaultRedisCache(RedisTemplate redisTemplate) { 31 | if(redisTemplate == null){ 32 | throw new IllegalArgumentException("Parameter redisTemplate cannot be empty."); 33 | } 34 | 35 | this.redisTemplate = redisTemplate; 36 | this.opsForHash = redisTemplate.opsForHash(); 37 | } 38 | 39 | @Override 40 | public Object get(String cacheName, Serializable key) throws CacheException { 41 | if(cacheName == null){ 42 | throw new IllegalArgumentException("Parameter cacheName cannot be empty."); 43 | } 44 | if (key == null) { 45 | throw new IllegalArgumentException("Parameter key cannot be empty."); 46 | } 47 | 48 | return opsForHash.get(cacheName, key.toString()); 49 | } 50 | 51 | @Override 52 | public void put(String cacheName, Serializable key, Object value) throws CacheException { 53 | if(cacheName == null){ 54 | throw new IllegalArgumentException("Parameter cacheName cannot be empty."); 55 | } 56 | if (key == null) { 57 | throw new IllegalArgumentException("Parameter key cannot be empty."); 58 | } 59 | 60 | if(value != null){ 61 | opsForHash.put(cacheName, key.toString(), value); 62 | }else{ 63 | opsForHash.delete(cacheName, key.toString()); 64 | } 65 | } 66 | 67 | @Override 68 | public void putAll(String cacheName, Map sources) throws CacheException { 69 | if(sources != null && sources.size() > 0){ 70 | opsForHash.putAll(cacheName, sources); 71 | } 72 | } 73 | 74 | @Override 75 | public void remove(String cacheName, Serializable key) throws CacheException { 76 | if(cacheName == null){ 77 | throw new IllegalArgumentException("Parameter cacheName cannot be empty."); 78 | } 79 | if (key == null) { 80 | throw new IllegalArgumentException("Parameter key cannot be empty."); 81 | } 82 | 83 | //数据Map 84 | opsForHash.delete(cacheName, key.toString()); 85 | } 86 | 87 | @Override 88 | public void clear(String cacheName) throws CacheException { 89 | if(cacheName == null){ 90 | throw new IllegalArgumentException("Parameter cacheName cannot be empty."); 91 | } 92 | 93 | this.redisTemplate.delete(cacheName); 94 | } 95 | 96 | @Override 97 | public int size(String cacheName) { 98 | if(cacheName == null){ 99 | throw new IllegalArgumentException("Parameter cacheName cannot be empty."); 100 | } 101 | 102 | return opsForHash.size(cacheName).intValue(); 103 | } 104 | 105 | @Override 106 | public Set keySet(String cacheName) { 107 | if(cacheName == null){ 108 | throw new IllegalArgumentException("Parameter cacheName cannot be empty."); 109 | } 110 | 111 | return opsForHash.keys(cacheName); 112 | } 113 | 114 | @Override 115 | public Collection values(String cacheName) { 116 | if(cacheName == null){ 117 | throw new IllegalArgumentException("Parameter cacheName cannot be empty."); 118 | } 119 | 120 | return opsForHash.values(cacheName); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/support/TokenInfo.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl.support; 2 | 3 | import cn.zifangsky.easylimit.common.Constants; 4 | import cn.zifangsky.easylimit.enums.DefaultTimeEnums; 5 | 6 | import java.time.temporal.ChronoUnit; 7 | 8 | /** 9 | * Token模式的参数设置 10 | * 11 | * @author zifangsky 12 | * @date 2019/5/31 13 | * @since 1.0.0 14 | */ 15 | public class TokenInfo { 16 | /** 17 | * Access Token的参数名 18 | */ 19 | private String accessTokenParamName; 20 | 21 | /** 22 | * Refresh Token的参数名 23 | */ 24 | private String refreshTokenParamName; 25 | 26 | /** 27 | * Access Token的过期时间的参数名 28 | */ 29 | private String expiresInParamName; 30 | 31 | /** 32 | * Access Token的超时时间 33 | */ 34 | private Long accessTokenTimeout; 35 | /** 36 | * Access Token的超时时间的单位 37 | */ 38 | private ChronoUnit accessTokenTimeoutUnit; 39 | 40 | /** 41 | * Refresh Token的超时时间 42 | */ 43 | private Long refreshTokenTimeout; 44 | /** 45 | * Refresh Token的超时时间的单位 46 | */ 47 | private ChronoUnit refreshTokenTimeoutUnit; 48 | 49 | public TokenInfo() { 50 | this.accessTokenParamName = Constants.DEFAULT_ACCESS_TOKEN_PARAM_NAME; 51 | this.refreshTokenParamName = Constants.DEFAULT_REFRESH_TOKEN_PARAM_NAME; 52 | this.expiresInParamName = Constants.DEFAULT_EXPIRES_IN_PARAM_NAME; 53 | this.accessTokenTimeout = DefaultTimeEnums.ACCESS_TOKEN.getTime(); 54 | this.accessTokenTimeoutUnit = DefaultTimeEnums.ACCESS_TOKEN.getChronoUnit(); 55 | this.refreshTokenTimeout = DefaultTimeEnums.REFRESH_TOKEN.getTime(); 56 | this.refreshTokenTimeoutUnit = DefaultTimeEnums.REFRESH_TOKEN.getChronoUnit(); 57 | } 58 | 59 | public String getAccessTokenParamName() { 60 | return accessTokenParamName; 61 | } 62 | 63 | public void setAccessTokenParamName(String accessTokenParamName) { 64 | this.accessTokenParamName = accessTokenParamName; 65 | } 66 | 67 | public String getRefreshTokenParamName() { 68 | return refreshTokenParamName; 69 | } 70 | 71 | public void setRefreshTokenParamName(String refreshTokenParamName) { 72 | this.refreshTokenParamName = refreshTokenParamName; 73 | } 74 | 75 | public String getExpiresInParamName() { 76 | return expiresInParamName; 77 | } 78 | 79 | public void setExpiresInParamName(String expiresInParamName) { 80 | this.expiresInParamName = expiresInParamName; 81 | } 82 | 83 | public Long getAccessTokenTimeout() { 84 | return accessTokenTimeout; 85 | } 86 | 87 | public void setAccessTokenTimeout(Long accessTokenTimeout) { 88 | this.accessTokenTimeout = accessTokenTimeout; 89 | } 90 | 91 | public ChronoUnit getAccessTokenTimeoutUnit() { 92 | return accessTokenTimeoutUnit; 93 | } 94 | 95 | public void setAccessTokenTimeoutUnit(ChronoUnit accessTokenTimeoutUnit) { 96 | this.accessTokenTimeoutUnit = accessTokenTimeoutUnit; 97 | } 98 | 99 | public Long getRefreshTokenTimeout() { 100 | return refreshTokenTimeout; 101 | } 102 | 103 | public void setRefreshTokenTimeout(Long refreshTokenTimeout) { 104 | this.refreshTokenTimeout = refreshTokenTimeout; 105 | } 106 | 107 | public ChronoUnit getRefreshTokenTimeoutUnit() { 108 | return refreshTokenTimeoutUnit; 109 | } 110 | 111 | public void setRefreshTokenTimeoutUnit(ChronoUnit refreshTokenTimeoutUnit) { 112 | this.refreshTokenTimeoutUnit = refreshTokenTimeoutUnit; 113 | } 114 | 115 | @Override 116 | public String toString() { 117 | return "TokenInfo{" + 118 | "accessTokenParamName='" + accessTokenParamName + '\'' + 119 | ", refreshTokenParamName='" + refreshTokenParamName + '\'' + 120 | ", expiresInParamName='" + expiresInParamName + '\'' + 121 | ", accessTokenTimeout=" + accessTokenTimeout + 122 | ", accessTokenTimeoutUnit=" + accessTokenTimeoutUnit + 123 | ", refreshTokenTimeout=" + refreshTokenTimeout + 124 | ", refreshTokenTimeoutUnit=" + refreshTokenTimeoutUnit + 125 | '}'; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/impl/DefaultLoginFilter.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter.impl; 2 | 3 | import cn.zifangsky.easylimit.DefaultWebSecurityManager; 4 | import cn.zifangsky.easylimit.SecurityManager; 5 | import cn.zifangsky.easylimit.access.Access; 6 | import cn.zifangsky.easylimit.common.Constants; 7 | import cn.zifangsky.easylimit.enums.DefaultTokenRespEnums; 8 | import cn.zifangsky.easylimit.enums.ProjectModeEnums; 9 | import cn.zifangsky.easylimit.filter.AbstractAccessControlFilter; 10 | import cn.zifangsky.easylimit.filter.impl.support.TokenRespMsg; 11 | import cn.zifangsky.easylimit.session.Session; 12 | import cn.zifangsky.easylimit.utils.SecurityUtils; 13 | import cn.zifangsky.easylimit.utils.WebUtils; 14 | 15 | import javax.servlet.Filter; 16 | import javax.servlet.http.HttpServletRequest; 17 | import javax.servlet.http.HttpServletResponse; 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | /** 22 | * 默认的登录{@link Filter} 23 | * 24 | * @author zifangsky 25 | * @date 2019/5/6 26 | * @since 1.0.0 27 | */ 28 | public class DefaultLoginFilter extends AbstractAccessControlFilter{ 29 | 30 | /** 31 | * Token模式的未登录情况的错误返回 32 | */ 33 | private TokenRespMsg unLoginRespMsg = new TokenRespMsg(DefaultTokenRespEnums.UN_LOGIN); 34 | 35 | /** 36 | * 被踢出情况的AJAX错误返回 37 | */ 38 | private TokenRespMsg kickOutRespMsg = new TokenRespMsg(DefaultTokenRespEnums.KICKOUT); 39 | 40 | @Override 41 | protected boolean isAccessAllowed(HttpServletRequest request, HttpServletResponse response, String[] controlVal) throws Exception { 42 | Access access = this.getAccess(request, response); 43 | SecurityManager securityManager = SecurityUtils.getSecurityManager(); 44 | 45 | if(securityManager instanceof DefaultWebSecurityManager){ 46 | DefaultWebSecurityManager defaultWebSecurityManager = (DefaultWebSecurityManager) securityManager; 47 | 48 | //如果已经设置“踢出当前用户的旧会话” 49 | if(defaultWebSecurityManager.isKickOutOldSessions()){ 50 | //1. 获取标识 51 | Session session = access.getSession(false); 52 | Object kickedOutFlag = session.getAttribute(Constants.KICK_OUT_OLD_SESSIONS_NAME); 53 | if(kickedOutFlag != null){ 54 | try { 55 | //2. 删除标识 56 | session.removeAttribute(Constants.KICK_OUT_OLD_SESSIONS_NAME); 57 | //3. 退出登录 58 | access.logout(); 59 | }catch (Exception e){ 60 | //ignore 61 | }finally { 62 | request.setAttribute(Constants.KICK_OUT_OLD_SESSIONS_NAME, true); 63 | } 64 | 65 | return false; 66 | } 67 | } 68 | } 69 | 70 | //返回是否已经登录成功的标识 71 | return access.isAuthenticated(); 72 | } 73 | 74 | @Override 75 | protected boolean afterAccessDenied(HttpServletRequest request, HttpServletResponse response, String[] controlVal) throws Exception { 76 | Object kickedOutFlag = request.getAttribute(Constants.KICK_OUT_OLD_SESSIONS_NAME); 77 | 78 | //需要跳转页面的情况 79 | if(ProjectModeEnums.DEFAULT.equals(this.getProjectMode()) && !WebUtils.isAjaxRequest(request)){ 80 | Map params = new HashMap<>(4); 81 | 82 | if(kickedOutFlag != null){ 83 | params.put(Constants.KICK_OUT_OLD_SESSIONS_PARAM_NAME, "1"); 84 | } 85 | 86 | //跳转到登录页面 87 | this.saveSourceUrlAndRedirectToLoginPage(request, response, params); 88 | } 89 | //其他情况返回AJAX提示 90 | else{ 91 | TokenRespMsg respMsg = null; 92 | 93 | if(kickedOutFlag != null){ 94 | respMsg = this.kickOutRespMsg; 95 | }else{ 96 | respMsg = this.unLoginRespMsg; 97 | } 98 | 99 | this.generateTokenResponse(response, respMsg); 100 | } 101 | 102 | return false; 103 | } 104 | 105 | public TokenRespMsg getUnLoginRespMsg() { 106 | return unLoginRespMsg; 107 | } 108 | 109 | public void setUnLoginRespMsg(TokenRespMsg unLoginRespMsg) { 110 | this.unLoginRespMsg = unLoginRespMsg; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/ExposedSession.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl; 2 | 3 | import cn.zifangsky.easylimit.exception.session.InvalidSessionException; 4 | import cn.zifangsky.easylimit.session.Session; 5 | import cn.zifangsky.easylimit.session.SessionKey; 6 | import cn.zifangsky.easylimit.session.SessionManager; 7 | 8 | import java.io.Serializable; 9 | import java.time.LocalDateTime; 10 | import java.time.temporal.ChronoUnit; 11 | import java.util.Collection; 12 | 13 | /** 14 | * 对外暴露的{@link Session} 15 | * 16 | * @author zifangsky 17 | * @date 2019/3/29 18 | * @since 1.0.0 19 | */ 20 | public class ExposedSession implements Session, Serializable { 21 | private static final long serialVersionUID = -8554129137035197796L; 22 | 23 | /** 24 | * 用于获取sessionId 25 | */ 26 | private final SessionKey sessionKey; 27 | 28 | /** 29 | * 将对{@link Session}的操作委托给{@link SessionManager} 30 | */ 31 | private final SessionManager sessionManager; 32 | 33 | public ExposedSession(SessionKey sessionKey, SessionManager sessionManager) { 34 | if (sessionKey == null) { 35 | throw new IllegalArgumentException("Parameter sessionKey cannot be empty."); 36 | } 37 | if (sessionManager == null) { 38 | throw new IllegalArgumentException("Parameter sessionManager cannot be empty."); 39 | } 40 | if (sessionKey.getSessionId() == null) { 41 | throw new IllegalArgumentException("The sessionId parameter in sessionManager cannot be empty."); 42 | } 43 | 44 | this.sessionKey = sessionKey; 45 | this.sessionManager = sessionManager; 46 | } 47 | 48 | @Override 49 | public Serializable getId() { 50 | return sessionKey.getSessionId(); 51 | } 52 | 53 | @Override 54 | public String getHost() { 55 | return sessionManager.getHost(sessionKey); 56 | } 57 | 58 | @Override 59 | public LocalDateTime getCreateTime() { 60 | return sessionManager.getCreateTime(sessionKey); 61 | } 62 | 63 | @Override 64 | public LocalDateTime getLatestAccessTime() { 65 | return sessionManager.getLatestAccessTime(sessionKey); 66 | } 67 | 68 | @Override 69 | public LocalDateTime getStopTime() { 70 | return sessionManager.getStopTime(sessionKey); 71 | } 72 | 73 | @Override 74 | public long getTimeout() throws InvalidSessionException { 75 | return sessionManager.getTimeout(sessionKey); 76 | } 77 | 78 | @Override 79 | public void setTimeout(long maxIdleTime) throws InvalidSessionException { 80 | sessionManager.setTimeout(sessionKey, maxIdleTime); 81 | } 82 | 83 | @Override 84 | public ChronoUnit getTimeoutChronoUnit() { 85 | return sessionManager.getTimeoutChronoUnit(sessionKey); 86 | } 87 | 88 | @Override 89 | public void setTimeoutChronoUnit(ChronoUnit timeoutChronoUnit) { 90 | sessionManager.setTimeoutChronoUnit(sessionKey, timeoutChronoUnit); 91 | } 92 | 93 | @Override 94 | public boolean isValid() { 95 | return sessionManager.isValid(sessionKey); 96 | } 97 | 98 | @Override 99 | public void refresh() throws InvalidSessionException { 100 | sessionManager.refresh(sessionKey); 101 | } 102 | 103 | @Override 104 | public void validate() throws InvalidSessionException { 105 | sessionManager.validate(sessionKey); 106 | } 107 | 108 | @Override 109 | public void stop() throws InvalidSessionException { 110 | sessionManager.stop(sessionKey); 111 | } 112 | 113 | @Override 114 | public Collection getAttributeNames() throws InvalidSessionException { 115 | return sessionManager.getAttributeNames(sessionKey); 116 | } 117 | 118 | @Override 119 | public Object getAttribute(String key) throws InvalidSessionException { 120 | return sessionManager.getAttribute(sessionKey, key); 121 | } 122 | 123 | @Override 124 | public void setAttribute(String key, Object value) throws InvalidSessionException { 125 | sessionManager.setAttribute(sessionKey, key, value); 126 | } 127 | 128 | @Override 129 | public void removeAttribute(String key) throws InvalidSessionException { 130 | sessionManager.removeAttribute(sessionKey, key); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/AbstractAccessControlFilter.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter; 2 | 3 | import cn.zifangsky.easylimit.SecurityManager; 4 | import cn.zifangsky.easylimit.access.Access; 5 | import cn.zifangsky.easylimit.common.Constants; 6 | import cn.zifangsky.easylimit.utils.SecurityUtils; 7 | import cn.zifangsky.easylimit.utils.WebUtils; 8 | 9 | import javax.servlet.Filter; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | import java.io.IOException; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | /** 17 | * 访问控制相关的抽象{@link Filter} 18 | * 19 | * @author zifangsky 20 | * @date 2019/4/30 21 | * @since 1.0.0 22 | */ 23 | public abstract class AbstractAccessControlFilter extends AbstractPathFilter{ 24 | /** 25 | * 登录URL 26 | */ 27 | private String loginUrl = Constants.DEFAULT_LOGIN_URL; 28 | 29 | /** 30 | * 登录校验URL 31 | */ 32 | private String loginCheckUrl = Constants.DEFAULT_LOGIN_CHECK_URL; 33 | 34 | /** 35 | * 是否允许访问 36 | * @author zifangsky 37 | * @date 2019/5/6 16:52 38 | * @since 1.0.0 39 | * @param request HttpServletRequest 40 | * @param response HttpServletResponse 41 | * @param controlVal 通过当前filter需要的角色、资源名称等 42 | * @return boolean 43 | * @throws Exception Exception 44 | */ 45 | protected abstract boolean isAccessAllowed(HttpServletRequest request, HttpServletResponse response, String[] controlVal) throws Exception; 46 | 47 | /** 48 | * 当没有访问许可时,可以做的其他事,比如: 49 | *
    50 | *
  • 执行登录流程
  • 51 | *
  • 返回指定提示信息
  • 52 | *
53 | * 54 | * @author zifangsky 55 | * @date 2019/5/6 16:54 56 | * @since 1.0.0 57 | * @param request HttpServletRequest 58 | * @param response HttpServletResponse 59 | * @param controlVal 通过当前filter需要的角色、资源名称等 60 | * @return boolean 61 | * @throws Exception Exception 62 | */ 63 | protected abstract boolean afterAccessDenied(HttpServletRequest request, HttpServletResponse response, String[] controlVal) throws Exception; 64 | 65 | @Override 66 | protected boolean doPreHandle(HttpServletRequest request, HttpServletResponse response, String[] controlVal) throws Exception { 67 | return this.isAccessAllowed(request, response, controlVal) 68 | || this.afterAccessDenied(request,response, controlVal); 69 | } 70 | 71 | /** 72 | *

1. 保存来源URL

73 | *

1. 重定向到登录页面

74 | */ 75 | protected void saveSourceUrlAndRedirectToLoginPage(HttpServletRequest request, HttpServletResponse response, 76 | Map params) throws IOException { 77 | String sourceUrl = this.saveSourceUrl(request); 78 | 79 | if(params == null){ 80 | params = new HashMap<>(4); 81 | } 82 | params.put(Constants.DEFAULT_REDIRECT_URL_NAME, sourceUrl); 83 | 84 | this.redirectToLoginPage(request, response, params); 85 | } 86 | 87 | /** 88 | * 保存来源URL,目的是方便登录成功之后跳转回去 89 | */ 90 | protected String saveSourceUrl(HttpServletRequest request){ 91 | return WebUtils.saveSourceUrl(request); 92 | } 93 | 94 | /** 95 | * 重定向到登录页面 96 | */ 97 | protected void redirectToLoginPage(HttpServletRequest request, HttpServletResponse response, 98 | Map params) throws IOException { 99 | this.doRedirect(request, response, this.loginUrl, params); 100 | } 101 | 102 | protected Access getAccess(HttpServletRequest request, HttpServletResponse response){ 103 | return SecurityUtils.getAccess(); 104 | } 105 | 106 | protected SecurityManager getSecurityManager(HttpServletRequest request, HttpServletResponse response){ 107 | return SecurityUtils.getSecurityManager(); 108 | } 109 | 110 | public String getLoginUrl() { 111 | return loginUrl; 112 | } 113 | 114 | public void setLoginUrl(String loginUrl) { 115 | this.loginUrl = loginUrl; 116 | } 117 | 118 | public String getLoginCheckUrl() { 119 | return loginCheckUrl; 120 | } 121 | 122 | public void setLoginCheckUrl(String loginCheckUrl) { 123 | this.loginCheckUrl = loginCheckUrl; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/AbstractPathFilter.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter; 2 | 3 | import cn.zifangsky.easylimit.utils.AntPathMatcher; 4 | import cn.zifangsky.easylimit.utils.PatternMatcher; 5 | import cn.zifangsky.easylimit.utils.WebUtils; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import javax.servlet.Filter; 10 | import javax.servlet.ServletRequest; 11 | import javax.servlet.ServletResponse; 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | import java.text.MessageFormat; 15 | import java.util.LinkedHashMap; 16 | import java.util.Map; 17 | 18 | /** 19 | * 路径校验相关的抽象{@link Filter} 20 | * 21 | * @author zifangsky 22 | * @date 2019/4/30 23 | * @since 1.0.0 24 | */ 25 | public abstract class AbstractPathFilter extends AbstractAdviceFilter{ 26 | private static final Logger LOGGER = LoggerFactory.getLogger(AbstractPathFilter.class); 27 | 28 | /** 29 | * 校验请求URI和预设的正则类型的拦截路径是否匹配 30 | */ 31 | protected PatternMatcher patternMatcher = new AntPathMatcher(); 32 | 33 | /** 34 | * 预设的正则类型的拦截路径 35 | *

示例:

36 | *

KEY: /css/**

37 | *

VALUE: admin,user

38 | */ 39 | protected Map patternPathMap = new LinkedHashMap<>(); 40 | 41 | /** 42 | * 给当前filter添加正则类型的拦截路径 43 | * @author zifangsky 44 | * @date 2019/4/30 15:37 45 | * @since 1.0.0 46 | * @param patternPath 拦截路径,比如:/css/** 47 | * @param controlVal 通过当前filter需要的角色、资源名称等,比如:admin,user 48 | * @return javax.servlet.Filter 49 | */ 50 | public Filter addPatternPathConfig(String patternPath, String[] controlVal){ 51 | if(!patternPathMap.containsKey(patternPath)){ 52 | patternPathMap.put(patternPath, controlVal); 53 | }else{ 54 | //如果可以覆盖,那么也设置 55 | if(this.overridePreviousPatternPath()){ 56 | patternPathMap.put(patternPath, controlVal); 57 | } 58 | } 59 | 60 | return this; 61 | } 62 | 63 | /** 64 | * preHandle方法的真正校验逻辑 65 | * @author zifangsky 66 | * @date 2019/4/30 15:13 67 | * @since 1.0.0 68 | * @param request HttpServletRequest 69 | * @param response HttpServletResponse 70 | * @param controlVal 通过当前filter需要的角色、资源名称等 71 | * @return boolean 72 | */ 73 | protected boolean doPreHandle(HttpServletRequest request, 74 | HttpServletResponse response, String[] controlVal) throws Exception{ 75 | return true; 76 | } 77 | 78 | @Override 79 | protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { 80 | if(patternPathMap == null || patternPathMap.isEmpty()){ 81 | LOGGER.debug("Because the pattern path is not preset, the filter will no longer intercept."); 82 | return true; 83 | } 84 | 85 | HttpServletRequest httpServletRequest = WebUtils.toHttp(request); 86 | HttpServletResponse httpServletResponse = WebUtils.toHttp(response); 87 | 88 | //查找正则类型的拦截路径,然后执行 89 | for(String patternPath : patternPathMap.keySet()){ 90 | if(this.matchPath(patternPath, httpServletRequest)){ 91 | //获取通过当前filter需要的角色、资源名称等 92 | String[] controlVal = patternPathMap.get(patternPath); 93 | 94 | LOGGER.debug(MessageFormat.format("Start executing the doPreHandle method, patternPath [{0}], controlVal [{1}].", patternPath, controlVal)); 95 | return this.doPreHandle(httpServletRequest, httpServletResponse, controlVal); 96 | } 97 | } 98 | 99 | return true; 100 | } 101 | 102 | /** 103 | * 校验请求URI和预设的正则类型的拦截路径是否匹配 104 | */ 105 | protected boolean matchPath(String patternPath, HttpServletRequest httpServletRequest){ 106 | //获取不包含当前域名和项目名的URI 107 | String servletPath = WebUtils.getServletPath(httpServletRequest); 108 | return this.matchPath(patternPath, servletPath); 109 | } 110 | 111 | /** 112 | * 校验请求URI和预设的正则类型的拦截路径是否匹配 113 | */ 114 | protected boolean matchPath(String patternPath, String servletPath){ 115 | return patternMatcher.match(patternPath, servletPath); 116 | } 117 | 118 | /** 119 | * 是否覆盖前面设置的 pattern path 120 | */ 121 | protected boolean overridePreviousPatternPath(){ 122 | return true; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/MemoryTokenDAO.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl; 2 | 3 | import cn.zifangsky.easylimit.authc.PrincipalInfo; 4 | import cn.zifangsky.easylimit.session.TokenDAO; 5 | import cn.zifangsky.easylimit.session.impl.support.SimpleAccessToken; 6 | import cn.zifangsky.easylimit.session.impl.support.SimpleRefreshToken; 7 | import cn.zifangsky.easylimit.utils.DateUtils; 8 | 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | /** 12 | * 使用内存存储 13 | * 14 | * @author zifangsky 15 | * @date 2019/6/3 16 | * @since 1.0.0 17 | */ 18 | public class MemoryTokenDAO implements TokenDAO{ 19 | 20 | /** 21 | * 使用{@link ConcurrentHashMap}存储所有{@link SimpleAccessToken} 22 | */ 23 | private ConcurrentHashMap accessTokenStorageMap; 24 | 25 | /** 26 | * 使用{@link ConcurrentHashMap}存储所有{@link SimpleRefreshToken} 27 | */ 28 | private ConcurrentHashMap refreshTokenStorageMap; 29 | 30 | public MemoryTokenDAO() { 31 | this.accessTokenStorageMap = new ConcurrentHashMap<>(); 32 | this.refreshTokenStorageMap = new ConcurrentHashMap<>(); 33 | } 34 | 35 | public MemoryTokenDAO(ConcurrentHashMap accessTokenStorageMap, ConcurrentHashMap refreshTokenStorageMap) { 36 | this.accessTokenStorageMap = accessTokenStorageMap; 37 | this.refreshTokenStorageMap = refreshTokenStorageMap; 38 | } 39 | 40 | @Override 41 | public SimpleAccessToken readByAccessToken(String accessToken) { 42 | return this.accessTokenStorageMap.get(accessToken); 43 | } 44 | 45 | @Override 46 | public SimpleRefreshToken readByRefreshToken(String refreshToken) { 47 | return this.refreshTokenStorageMap.get(refreshToken); 48 | } 49 | 50 | @Override 51 | public void updateAccessToken(SimpleAccessToken accessToken) { 52 | if(accessToken == null || accessToken.getAccessToken() == null){ 53 | throw new IllegalArgumentException("Parameter accessToken cannot be empty."); 54 | } 55 | 56 | //设置最新访问时间 57 | accessToken.setLatestAccessTime(DateUtils.now()); 58 | 59 | this.accessTokenStorageMap.put(accessToken.getAccessToken(), accessToken); 60 | } 61 | 62 | @Override 63 | public void updateRefreshToken(SimpleRefreshToken refreshToken) { 64 | if(refreshToken == null || refreshToken.getRefreshToken() == null){ 65 | throw new IllegalArgumentException("Parameter refreshToken cannot be empty."); 66 | } 67 | 68 | //设置最新访问时间 69 | refreshToken.setLatestAccessTime(DateUtils.now()); 70 | 71 | this.refreshTokenStorageMap.put(refreshToken.getRefreshToken(), refreshToken); 72 | } 73 | 74 | @Override 75 | public void deleteAccessToken(String accessToken) { 76 | if(accessToken == null){ 77 | throw new IllegalArgumentException("Parameter accessToken cannot be empty."); 78 | } 79 | 80 | this.accessTokenStorageMap.remove(accessToken); 81 | } 82 | 83 | @Override 84 | public void deleteRefreshToken(String refreshToken) { 85 | if(refreshToken == null){ 86 | throw new IllegalArgumentException("Parameter refreshToken cannot be empty."); 87 | } 88 | 89 | this.refreshTokenStorageMap.remove(refreshToken); 90 | } 91 | 92 | @Override 93 | public void deleteOldAccessToken(String account) { 94 | if(account != null && this.accessTokenStorageMap != null){ 95 | this.accessTokenStorageMap.forEach((key, value) -> { 96 | PrincipalInfo principalInfo = value.getPrincipalInfo(); 97 | //删除该用户的历史Access Token 98 | if(principalInfo != null && account.equals(principalInfo.getAccount())){ 99 | this.accessTokenStorageMap.remove(key); 100 | } 101 | }); 102 | } 103 | } 104 | 105 | @Override 106 | public void deleteOldRefreshToken(String accessToken) { 107 | if(accessToken != null && this.refreshTokenStorageMap != null){ 108 | this.refreshTokenStorageMap.forEach((key, value) -> { 109 | //删除某个Access Token关联的所有Refresh Token 110 | if(accessToken.equals(value.getAccessToken())){ 111 | this.refreshTokenStorageMap.remove(key); 112 | } 113 | }); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/AbstractFilter.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter; 2 | 3 | import javax.servlet.Filter; 4 | import javax.servlet.FilterConfig; 5 | import javax.servlet.ServletContext; 6 | import javax.servlet.ServletException; 7 | 8 | /** 9 | * {@link Filter}的基本实现 10 | * 11 | * @author zifangsky 12 | * @date 2019/4/29 13 | * @since 1.0.0 14 | */ 15 | public abstract class AbstractFilter implements Filter{ 16 | /** 17 | * ServletContext 18 | */ 19 | private ServletContext servletContext = null; 20 | 21 | /** 22 | * ServletContext 23 | */ 24 | private FilterConfig filterConfig; 25 | 26 | /** 27 | * {@link Filter}的名称 28 | */ 29 | private String filterName; 30 | 31 | /** 32 | * 允许子类在初始化{@link Filter}时做一些其他操作 33 | * @author zifangsky 34 | * @date 2019/4/29 11:15 35 | * @since 1.0.0 36 | */ 37 | protected void onInitFilter() throws ServletException{ 38 | 39 | } 40 | 41 | @Override 42 | public void init(FilterConfig filterConfig) throws ServletException { 43 | this.setFilterConfig(filterConfig); 44 | this.onInitFilter(); 45 | } 46 | 47 | @Override 48 | public void destroy() { 49 | 50 | } 51 | 52 | /** 53 | * 获取{@link Filter}的初始参数 54 | * @author zifangsky 55 | * @date 2019/4/29 11:11 56 | * @since 1.0.0 57 | * @param paramName 参数名 58 | * @return java.lang.String 59 | */ 60 | public String getInitParameter(String paramName){ 61 | FilterConfig config = this.filterConfig; 62 | if(config != null){ 63 | return config.getInitParameter(paramName); 64 | } 65 | 66 | return null; 67 | } 68 | 69 | /** 70 | * 向{@link ServletContext}设置键值对 71 | * @author zifangsky 72 | * @date 2019/4/29 10:58 73 | * @since 1.0.0 74 | * @param key KEY 75 | * @param value VALUE 76 | */ 77 | public void setContextAttribute(String key, Object value) { 78 | if (value == null) { 79 | this.removeContextAttribute(key); 80 | } else { 81 | this.acquireServletContext().setAttribute(key, value); 82 | } 83 | } 84 | 85 | /** 86 | * 从{@link ServletContext}获取键值对 87 | * @author zifangsky 88 | * @date 2019/4/29 10:59 89 | * @since 1.0.0 90 | * @param key KEY 91 | * @return java.lang.Object 92 | */ 93 | public Object getContextAttribute(String key) { 94 | return this.acquireServletContext().getAttribute(key); 95 | } 96 | 97 | /** 98 | * 从{@link ServletContext}移除键值对 99 | * @author zifangsky 100 | * @date 2019/4/29 11:00 101 | * @since 1.0.0 102 | * @param key KEY 103 | */ 104 | public void removeContextAttribute(String key) { 105 | this.acquireServletContext().removeAttribute(key); 106 | } 107 | 108 | /** 109 | * 获取{@link ServletContext},如果不存在则抛出异常 110 | * @author zifangsky 111 | * @date 2019/4/29 10:53 112 | * @since 1.0.0 113 | * @return javax.servlet.ServletContext 114 | */ 115 | protected ServletContext acquireServletContext(){ 116 | ServletContext context = this.servletContext; 117 | 118 | if(context == null){ 119 | throw new IllegalArgumentException("Parameter servletContext cannot be empty."); 120 | } 121 | 122 | return context; 123 | } 124 | 125 | public ServletContext getServletContext() { 126 | return servletContext; 127 | } 128 | 129 | public void setServletContext(ServletContext servletContext) { 130 | this.servletContext = servletContext; 131 | } 132 | 133 | public FilterConfig getFilterConfig() { 134 | return filterConfig; 135 | } 136 | 137 | public void setFilterConfig(FilterConfig filterConfig) { 138 | if(filterConfig != null){ 139 | this.filterConfig = filterConfig; 140 | this.setServletContext(filterConfig.getServletContext()); 141 | } 142 | } 143 | 144 | public String getFilterName() { 145 | if(this.filterName == null){ 146 | FilterConfig config = this.filterConfig; 147 | if(config != null){ 148 | this.filterName = config.getFilterName(); 149 | } 150 | } 151 | 152 | return this.filterName; 153 | } 154 | 155 | public void setFilterName(String filterName) { 156 | this.filterName = filterName; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/impl/support/TokenProxiedFilter.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter.impl.support; 2 | 3 | import cn.zifangsky.easylimit.SecurityManager; 4 | import cn.zifangsky.easylimit.access.Access; 5 | import cn.zifangsky.easylimit.access.impl.TokenAccessContext; 6 | import cn.zifangsky.easylimit.enums.DefaultTokenRespEnums; 7 | import cn.zifangsky.easylimit.exception.authc.NoPermissionException; 8 | import cn.zifangsky.easylimit.exception.authc.NoRoleException; 9 | import cn.zifangsky.easylimit.exception.authc.NotLoginException; 10 | import cn.zifangsky.easylimit.exception.token.ExpiredTokenException; 11 | import cn.zifangsky.easylimit.exception.token.InvalidTokenException; 12 | import cn.zifangsky.easylimit.filter.FilterChainResolver; 13 | import cn.zifangsky.easylimit.utils.WebUtils; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import javax.servlet.FilterChain; 18 | import javax.servlet.ServletException; 19 | import javax.servlet.http.HttpServletRequest; 20 | import javax.servlet.http.HttpServletResponse; 21 | import java.io.IOException; 22 | 23 | /** 24 | * 基于Token模式的{@link AbstractProxiedFilter} 25 | * 26 | * @author zifangsky 27 | * @date 2019/5/15 28 | * @since 1.0.0 29 | */ 30 | public class TokenProxiedFilter extends AbstractProxiedFilter { 31 | private static final Logger LOGGER = LoggerFactory.getLogger(TokenProxiedFilter.class); 32 | 33 | /** 34 | * Token模式的“TOKEN不可用”情况的错误返回 35 | */ 36 | private TokenRespMsg invalidTokenRespMsg = new TokenRespMsg(DefaultTokenRespEnums.INVALID_TOKEN); 37 | 38 | /** 39 | * Token模式的“TOKEN过期”情况的错误返回 40 | */ 41 | private TokenRespMsg expiredTokenRespMsg = new TokenRespMsg(DefaultTokenRespEnums.EXPIRED_TOKEN); 42 | 43 | /** 44 | * Token模式的“没有指定权限”情况的错误返回 45 | */ 46 | private TokenRespMsg noPermissionsRespMsg = new TokenRespMsg(DefaultTokenRespEnums.NO_PERMISSIONS); 47 | 48 | /** 49 | * Token模式的“未登录”情况的错误返回 50 | */ 51 | private TokenRespMsg notLoginRespMsg = new TokenRespMsg(DefaultTokenRespEnums.UN_LOGIN); 52 | 53 | /** 54 | * Token模式的“系统异常”情况的错误返回 55 | */ 56 | private TokenRespMsg systemErrorRespMsg = new TokenRespMsg(DefaultTokenRespEnums.SYSTEM_ERROR); 57 | 58 | 59 | public TokenProxiedFilter(SecurityManager securityManager, FilterChainResolver filterChainResolver) { 60 | super(securityManager, filterChainResolver); 61 | } 62 | 63 | @Override 64 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain originalChain) throws ServletException, IOException { 65 | try { 66 | //1. 创建访问实例 67 | Access access = this.createAccess(request, response); 68 | 69 | //2. 执行过滤链 70 | access.execute(() -> { 71 | executeFilterChain(request, response, originalChain); 72 | 73 | return null; 74 | }); 75 | }catch (Exception e){ 76 | TokenRespMsg tokenRespMsg = this.systemErrorRespMsg; 77 | 78 | //返回token不可用的提示 79 | if(e instanceof InvalidTokenException){ 80 | tokenRespMsg = this.invalidTokenRespMsg; 81 | } 82 | //返回token过期的提示 83 | else if(e instanceof ExpiredTokenException){ 84 | tokenRespMsg = this.expiredTokenRespMsg; 85 | } 86 | else{ 87 | Throwable cause = e.getCause().getCause(); 88 | if(cause != null){ 89 | //返回没有权限访问的提示 90 | if ((cause instanceof NoPermissionException) || (cause instanceof NoRoleException)){ 91 | tokenRespMsg = this.noPermissionsRespMsg; 92 | } 93 | //返回未登录的提示 94 | else if((cause instanceof NotLoginException)){ 95 | tokenRespMsg = this.notLoginRespMsg; 96 | } 97 | } 98 | } 99 | 100 | try { 101 | this.generateTokenResponse(WebUtils.toHttp(response), tokenRespMsg); 102 | } catch (Exception ex) { 103 | //ignore 104 | } 105 | } 106 | } 107 | 108 | @Override 109 | protected Access createAccess(HttpServletRequest request, HttpServletResponse response) throws Exception { 110 | return new Access.Builder(this.getSecurityManager(), new TokenAccessContext(), request, response).build(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/permission/aop/PermissionsAnnotationAdvisor.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.permission.aop; 2 | 3 | import cn.zifangsky.easylimit.permission.annotation.RequiresLogin; 4 | import cn.zifangsky.easylimit.permission.annotation.RequiresPermissions; 5 | import cn.zifangsky.easylimit.permission.annotation.RequiresRoles; 6 | import org.aopalliance.aop.Advice; 7 | import org.springframework.aop.aspectj.AspectJExpressionPointcut; 8 | import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor; 9 | import org.springframework.core.annotation.AnnotationUtils; 10 | 11 | import java.lang.annotation.Annotation; 12 | import java.lang.reflect.Method; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /** 17 | * 权限注解处理的AOP通知 18 | * 19 | * @author zifangsky 20 | * @date 2019/6/20 21 | * @since 1.0.0 22 | */ 23 | public class PermissionsAnnotationAdvisor extends StaticMethodMatcherPointcutAdvisor { 24 | private static final long serialVersionUID = 3336828123978825653L; 25 | 26 | /** 27 | * 权限注解的列表 28 | */ 29 | private final List> ANNOTATION_LIST; 30 | 31 | /** 32 | * AOP切面的表达式 33 | */ 34 | private String aopExpression; 35 | 36 | public PermissionsAnnotationAdvisor(String aopExpression) { 37 | this(aopExpression, new DefaultAnnotationMethodInterceptor(), new ArrayList<>()); 38 | } 39 | 40 | public PermissionsAnnotationAdvisor(String aopExpression, List> annotationList) { 41 | this(aopExpression, new DefaultAnnotationMethodInterceptor(), annotationList); 42 | } 43 | 44 | public PermissionsAnnotationAdvisor(String aopExpression, Advice advice, List> annotationList) { 45 | if(aopExpression == null){ 46 | throw new IllegalArgumentException("Parameter aopExpression cannot be empty."); 47 | } 48 | if(advice == null){ 49 | throw new IllegalArgumentException("Parameter advice cannot be empty."); 50 | } 51 | if(annotationList == null){ 52 | throw new IllegalArgumentException("Parameter annotationList cannot be empty."); 53 | } 54 | 55 | this.aopExpression = aopExpression; 56 | this.ANNOTATION_LIST = annotationList; 57 | 58 | this.setAdvice(advice); 59 | //添加几个默认的权限注解 60 | this.addDefaultAnnotations(); 61 | //设置默认的ClassFilter 62 | this.setDefaultClassFilter(); 63 | } 64 | 65 | /** 66 | * 设置默认的ClassFilter 67 | */ 68 | protected void setDefaultClassFilter(){ 69 | AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); 70 | pointcut.setExpression(this.aopExpression); 71 | 72 | this.setClassFilter(pointcut); 73 | } 74 | 75 | /** 76 | * 添加几个默认的权限注解 77 | */ 78 | protected void addDefaultAnnotations(){ 79 | this.ANNOTATION_LIST.add(RequiresLogin.class); 80 | this.ANNOTATION_LIST.add(RequiresRoles.class); 81 | this.ANNOTATION_LIST.add(RequiresPermissions.class); 82 | } 83 | 84 | 85 | @Override 86 | public boolean matches(Method method, Class targetClass) { 87 | Method tmpMethod = method; 88 | 89 | if(this.isPermissionsAnnotation(tmpMethod)){ 90 | return true; 91 | } 92 | 93 | //查找实现类中是否存在权限注解 94 | if(targetClass != null){ 95 | try { 96 | tmpMethod = targetClass.getMethod(tmpMethod.getName(), tmpMethod.getParameterTypes()); 97 | return this.isPermissionsAnnotation(tmpMethod) || this.isPermissionsAnnotation(targetClass); 98 | } catch (NoSuchMethodException e) { 99 | //ignore 100 | } 101 | } 102 | 103 | return false; 104 | } 105 | 106 | /** 107 | * 查找是否存在权限注解 108 | */ 109 | private boolean isPermissionsAnnotation(Method method){ 110 | for(Class clazz : this.ANNOTATION_LIST){ 111 | Annotation annotation = AnnotationUtils.findAnnotation(method, clazz); 112 | if (annotation != null) { 113 | return true; 114 | } 115 | } 116 | 117 | return false; 118 | } 119 | 120 | /** 121 | * 查找是否存在权限注解 122 | */ 123 | private boolean isPermissionsAnnotation(Class targetClazz){ 124 | for(Class clazz : this.ANNOTATION_LIST){ 125 | Annotation annotation = AnnotationUtils.findAnnotation(targetClazz, clazz); 126 | if (annotation != null) { 127 | return true; 128 | } 129 | } 130 | 131 | return false; 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/filter/AbstractAdviceFilter.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.filter; 2 | 3 | import cn.zifangsky.easylimit.enums.ProjectModeEnums; 4 | import cn.zifangsky.easylimit.utils.WebUtils; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import javax.servlet.Filter; 9 | import javax.servlet.FilterChain; 10 | import javax.servlet.ServletException; 11 | import javax.servlet.ServletRequest; 12 | import javax.servlet.ServletResponse; 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.servlet.http.HttpServletResponse; 15 | import java.io.IOException; 16 | import java.text.MessageFormat; 17 | import java.util.Map; 18 | 19 | /** 20 | * 登录、角色、权限等业务功能的抽象{@link Filter} 21 | * 22 | * @author zifangsky 23 | * @date 2019/4/30 24 | * @since 1.0.0 25 | */ 26 | public abstract class AbstractAdviceFilter extends AbstractOncePerRequestFilter{ 27 | private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAdviceFilter.class); 28 | 29 | /** 30 | * 项目模式 31 | */ 32 | private ProjectModeEnums projectMode = ProjectModeEnums.DEFAULT; 33 | 34 | /** 35 | * 默认不做处理,继续执行后面的过滤链 36 | */ 37 | protected void executeFilterChain(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 38 | filterChain.doFilter(request, response); 39 | } 40 | 41 | /** 42 | * 校验某些条件,通过返回值判断是否继续执行后面的过滤链 43 | */ 44 | protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { 45 | return true; 46 | } 47 | 48 | /** 49 | * 请求返回之前执行某些操作 50 | */ 51 | protected void postHandle(ServletRequest request, ServletResponse response) throws Exception { 52 | 53 | } 54 | 55 | /** 56 | * 请求结束之后执行某些操作 57 | */ 58 | protected void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception { 59 | 60 | } 61 | 62 | @Override 63 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 64 | Exception exception = null; 65 | try { 66 | //1. 校验是否继续执行后面的过滤链 67 | boolean continueChain = this.preHandle(request, response); 68 | LOGGER.debug(MessageFormat.format("The preHandle method for [{0} filter] has been executed and the result is [{1}].", this.getFilterName(), continueChain)); 69 | 70 | //2. 如果结果为true,则继续执行后面的过滤链 71 | if(continueChain){ 72 | this.executeFilterChain(request, response, filterChain); 73 | LOGGER.debug(MessageFormat.format("The executeFilterChain method for [{0} filter] has been executed.", this.getFilterName())); 74 | } 75 | 76 | //3. 请求返回之前执行某些操作 77 | this.postHandle(request, response); 78 | LOGGER.debug(MessageFormat.format("The postHandle method for [{0} filter] has been executed.", this.getFilterName())); 79 | }catch (Exception e){ 80 | exception = e; 81 | }finally { 82 | //4. 执行清理工作 83 | this.cleanup(request, response, exception); 84 | } 85 | } 86 | 87 | /** 88 | * 执行清理工作 89 | */ 90 | protected void cleanup(ServletRequest request, ServletResponse response, Exception exception) 91 | throws ServletException, IOException { 92 | try { 93 | this.afterCompletion(request, response, exception); 94 | LOGGER.debug(MessageFormat.format("The afterCompletion method for [{0} filter] has been executed.", this.getFilterName())); 95 | } catch (Exception e) { 96 | LOGGER.error("The afterCompletion method threw an exception.", e); 97 | 98 | if(exception == null){ 99 | exception = e; 100 | } 101 | } 102 | 103 | if(exception != null){ 104 | if(exception instanceof ServletException){ 105 | throw (ServletException)exception; 106 | }else if(exception instanceof IOException){ 107 | throw (IOException)exception; 108 | }else{ 109 | LOGGER.error("Filter returns an unexpected exception during execution.", exception); 110 | throw new ServletException(exception); 111 | } 112 | } 113 | } 114 | 115 | /** 116 | * 重定向 117 | */ 118 | protected void doRedirect(HttpServletRequest request, HttpServletResponse response, 119 | String redirectUrl, Map params) throws IOException { 120 | WebUtils.executeRedirect(request, response, redirectUrl, params); 121 | } 122 | 123 | public ProjectModeEnums getProjectMode() { 124 | return projectMode; 125 | } 126 | 127 | public void setProjectMode(ProjectModeEnums projectMode) { 128 | this.projectMode = projectMode; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/cn/zifangsky/easylimit/session/impl/support/SimpleAccessToken.java: -------------------------------------------------------------------------------- 1 | package cn.zifangsky.easylimit.session.impl.support; 2 | 3 | import cn.zifangsky.easylimit.authc.PrincipalInfo; 4 | import com.fasterxml.jackson.annotation.JsonFormat; 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 8 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 9 | import org.springframework.format.annotation.DateTimeFormat; 10 | 11 | import java.io.Serializable; 12 | import java.time.LocalDateTime; 13 | 14 | /** 15 | * Access Token实例 16 | * 17 | * @author zifangsky 18 | * @date 2019/6/3 19 | * @since 1.0.0 20 | */ 21 | public class SimpleAccessToken { 22 | /** 23 | * Access Token 24 | */ 25 | private String accessToken; 26 | 27 | /** 28 | * 过期时间(单位为秒) 29 | */ 30 | private Long expiresIn; 31 | 32 | /** 33 | * 关联的用户主体信息 34 | */ 35 | private PrincipalInfo principalInfo; 36 | 37 | /** 38 | * 关联的会话ID 39 | */ 40 | private Serializable sessionId; 41 | 42 | /** 43 | * 是否已经过期 44 | */ 45 | private boolean expired = false; 46 | 47 | /** 48 | * 创建时间 49 | */ 50 | @JsonSerialize(using = LocalDateTimeSerializer.class) 51 | @JsonDeserialize(using = LocalDateTimeDeserializer.class) 52 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 53 | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") 54 | private LocalDateTime createTime; 55 | 56 | /** 57 | * 最新访问时间 58 | */ 59 | @JsonSerialize(using = LocalDateTimeSerializer.class) 60 | @JsonDeserialize(using = LocalDateTimeDeserializer.class) 61 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 62 | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") 63 | private LocalDateTime latestAccessTime; 64 | 65 | /** 66 | * 停用时间 67 | */ 68 | @JsonSerialize(using = LocalDateTimeSerializer.class) 69 | @JsonDeserialize(using = LocalDateTimeDeserializer.class) 70 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 71 | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") 72 | private LocalDateTime stopTime; 73 | 74 | public SimpleAccessToken() { 75 | 76 | } 77 | 78 | public SimpleAccessToken(String accessToken, Long expiresIn, PrincipalInfo principalInfo, Serializable sessionId, LocalDateTime createTime) { 79 | this.accessToken = accessToken; 80 | this.expiresIn = expiresIn; 81 | this.principalInfo = principalInfo; 82 | this.sessionId = sessionId; 83 | 84 | this.createTime = createTime; 85 | this.latestAccessTime = createTime; 86 | } 87 | 88 | public String getAccessToken() { 89 | return accessToken; 90 | } 91 | 92 | public void setAccessToken(String accessToken) { 93 | this.accessToken = accessToken; 94 | } 95 | 96 | public Long getExpiresIn() { 97 | return expiresIn; 98 | } 99 | 100 | public void setExpiresIn(Long expiresIn) { 101 | this.expiresIn = expiresIn; 102 | } 103 | 104 | public PrincipalInfo getPrincipalInfo() { 105 | return principalInfo; 106 | } 107 | 108 | public void setPrincipalInfo(PrincipalInfo principalInfo) { 109 | this.principalInfo = principalInfo; 110 | } 111 | 112 | public Serializable getSessionId() { 113 | return sessionId; 114 | } 115 | 116 | public void setSessionId(Serializable sessionId) { 117 | this.sessionId = sessionId; 118 | } 119 | 120 | public LocalDateTime getCreateTime() { 121 | return createTime; 122 | } 123 | 124 | public void setCreateTime(LocalDateTime createTime) { 125 | this.createTime = createTime; 126 | } 127 | 128 | public LocalDateTime getLatestAccessTime() { 129 | return latestAccessTime; 130 | } 131 | 132 | public void setLatestAccessTime(LocalDateTime latestAccessTime) { 133 | this.latestAccessTime = latestAccessTime; 134 | } 135 | 136 | public LocalDateTime getStopTime() { 137 | return stopTime; 138 | } 139 | 140 | public void setStopTime(LocalDateTime stopTime) { 141 | this.stopTime = stopTime; 142 | } 143 | 144 | public boolean isExpired() { 145 | return expired; 146 | } 147 | 148 | public void setExpired(boolean expired) { 149 | this.expired = expired; 150 | } 151 | 152 | @Override 153 | public String toString() { 154 | return "SimpleAccessToken{" + 155 | "accessToken='" + accessToken + '\'' + 156 | ", expiresIn=" + expiresIn + 157 | ", principalInfo=" + principalInfo + 158 | ", sessionId=" + sessionId + 159 | ", expired=" + expired + 160 | ", createTime=" + createTime + 161 | ", latestAccessTime=" + latestAccessTime + 162 | ", stopTime=" + stopTime + 163 | '}'; 164 | } 165 | } 166 | --------------------------------------------------------------------------------