diff --git a/powerjob-common/src/main/java/tech/powerjob/common/Loggers.java b/powerjob-common/src/main/java/tech/powerjob/common/Loggers.java new file mode 100644 index 00000000..5cc13748 --- /dev/null +++ b/powerjob-common/src/main/java/tech/powerjob/common/Loggers.java @@ -0,0 +1,18 @@ +package tech.powerjob.common; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 统一定义日志 + * + * @author tjq + * @since 2023/3/25 + */ +public class Loggers { + + /** + * Web 层统一日志 + */ + public static final Logger WEB = LoggerFactory.getLogger("P_SERVER_LOGGER_WEB"); +} diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/LoginContext.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/LoginContext.java similarity index 82% rename from powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/LoginContext.java rename to powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/LoginContext.java index 6020cd05..d06201bd 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/LoginContext.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/LoginContext.java @@ -1,7 +1,8 @@ -package tech.powerjob.server.auth.login; +package tech.powerjob.server.auth; import lombok.Getter; import lombok.Setter; +import lombok.experimental.Accessors; import javax.servlet.http.HttpServletRequest; @@ -13,6 +14,7 @@ import javax.servlet.http.HttpServletRequest; */ @Getter @Setter +@Accessors(chain = true) public class LoginContext { private HttpServletRequest httpServletRequest; diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/anno/ApiPermission.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/anno/ApiPermission.java index f94865cd..e30f7fb3 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/anno/ApiPermission.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/anno/ApiPermission.java @@ -2,12 +2,19 @@ package tech.powerjob.server.auth.anno; import tech.powerjob.server.auth.Permission; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + /** * API 权限 * * @author tjq * @since 2023/3/20 */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) public @interface ApiPermission { /** * API 名称 diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/PowerJobAuthInterceptor.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/PowerJobAuthInterceptor.java new file mode 100644 index 00000000..7d67789d --- /dev/null +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/PowerJobAuthInterceptor.java @@ -0,0 +1,82 @@ +package tech.powerjob.server.auth.interceptor; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; +import tech.powerjob.common.Loggers; +import tech.powerjob.common.exception.PowerJobException; +import tech.powerjob.server.auth.PowerJobUser; +import tech.powerjob.server.auth.anno.ApiPermission; +import tech.powerjob.server.auth.service.PowerJobAuthService; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.lang.reflect.Method; +import java.util.Optional; + +/** + * login auth and permission check + * + * @author tjq + * @since 2023/3/25 + */ +@Component +public class PowerJobAuthInterceptor implements HandlerInterceptor { + + @Resource + private PowerJobAuthService powerJobAuthService; + + @Override + public boolean preHandle(@NonNull HttpServletRequest request,@NonNull HttpServletResponse response,@NonNull Object handler) throws Exception { + if (!(handler instanceof HandlerMethod)) { + return true; + } + HandlerMethod handlerMethod = (HandlerMethod) handler; + final Method method = handlerMethod.getMethod(); + final ApiPermission apiPermissionAnno = method.getAnnotation(ApiPermission.class); + + // 无注解代表不需要权限,无需登陆直接访问 + if (apiPermissionAnno == null) { + return true; + } + + // 尝试直接解析登陆 + final Optional loginUserOpt = powerJobAuthService.parse(request); + + // 未登录前先使用302重定向到登录页面 + if (!loginUserOpt.isPresent()) { + response.setStatus(302); + response.setHeader("location", request.getContextPath() + "/login"); + return false; + } + + // 登陆用户进行权限校验 + final PowerJobUser powerJobUser = loginUserOpt.get(); + final boolean hasPermission = powerJobAuthService.hasPermission(powerJobUser, apiPermissionAnno); + if (hasPermission) { + return true; + } + + final String resourceName = parseResourceName(apiPermissionAnno, handlerMethod); + Loggers.WEB.info("[PowerJobAuthInterceptor] user[{}] has no permission to access: {}", powerJobUser.getUsername(), resourceName); + + throw new PowerJobException("Permission denied!"); + } + + private static String parseResourceName(ApiPermission apiPermission, HandlerMethod handlerMethod) { + final String name = apiPermission.name(); + if (StringUtils.isNotEmpty(name)) { + return name; + } + try { + final String clzName = handlerMethod.getBean().getClass().getSimpleName(); + final String methodName = handlerMethod.getMethod().getName(); + return String.format("%s_%s", clzName, methodName); + } catch (Exception ignore) { + } + return "UNKNOWN"; + } +} diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/biz/BizLoginService.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/BizLoginService.java similarity index 81% rename from powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/biz/BizLoginService.java rename to powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/BizLoginService.java index 0c33bb8d..01d2afa3 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/biz/BizLoginService.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/BizLoginService.java @@ -1,6 +1,6 @@ -package tech.powerjob.server.auth.login.biz; +package tech.powerjob.server.auth.login; -import tech.powerjob.server.auth.login.LoginContext; +import tech.powerjob.server.auth.LoginContext; import java.util.Optional; diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/biz/BizUser.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/BizUser.java similarity index 86% rename from powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/biz/BizUser.java rename to powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/BizUser.java index 31d27bc9..505f521f 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/biz/BizUser.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/BizUser.java @@ -1,4 +1,4 @@ -package tech.powerjob.server.auth.login.biz; +package tech.powerjob.server.auth.login; import lombok.Getter; import lombok.Setter; diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/biz/impl/DefaultBizLoginService.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/DefaultBizLoginService.java similarity index 79% rename from powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/biz/impl/DefaultBizLoginService.java rename to powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/DefaultBizLoginService.java index a33b7cb0..e149b34e 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/biz/impl/DefaultBizLoginService.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/DefaultBizLoginService.java @@ -1,12 +1,12 @@ -package tech.powerjob.server.auth.login.biz.impl; +package tech.powerjob.server.auth.login.impl; -import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; +import tech.powerjob.common.Loggers; import tech.powerjob.common.utils.DigestUtils; -import tech.powerjob.server.auth.login.LoginContext; -import tech.powerjob.server.auth.login.biz.BizLoginService; -import tech.powerjob.server.auth.login.biz.BizUser; +import tech.powerjob.server.auth.LoginContext; +import tech.powerjob.server.auth.login.BizLoginService; +import tech.powerjob.server.auth.login.BizUser; import tech.powerjob.server.common.SJ; import tech.powerjob.server.persistence.remote.model.UserInfoDO; import tech.powerjob.server.persistence.remote.repository.UserInfoRepository; @@ -21,7 +21,6 @@ import java.util.Optional; * @author tjq * @since 2023/3/20 */ -@Slf4j @Service public class DefaultBizLoginService implements BizLoginService { @@ -51,13 +50,13 @@ public class DefaultBizLoginService implements BizLoginService { final String password = loginInfoMap.get(KEY_PASSWORD); if (StringUtils.isAnyEmpty(username, password)) { - log.debug("[DefaultBizLoginService] username or password is empty, login failed!"); + Loggers.WEB.debug("[DefaultBizLoginService] username or password is empty, login failed!"); return Optional.empty(); } final Optional userInfoOpt = userInfoRepository.findByUsername(username); if (!userInfoOpt.isPresent()) { - log.debug("[DefaultBizLoginService] can't find user by username: {}", username); + Loggers.WEB.debug("[DefaultBizLoginService] can't find user by username: {}", username); return Optional.empty(); } @@ -69,7 +68,7 @@ public class DefaultBizLoginService implements BizLoginService { return Optional.of(bizUser); } - log.debug("[DefaultBizLoginService] user[{}]'s password is not correct, login failed!", username); + Loggers.WEB.debug("[DefaultBizLoginService] user[{}]'s password is not correct, login failed!", username); return Optional.empty(); } diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/PowerJobLoginService.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/PowerJobAuthService.java similarity index 55% rename from powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/PowerJobLoginService.java rename to powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/PowerJobAuthService.java index 7b9acca8..20de7fd9 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/PowerJobLoginService.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/PowerJobAuthService.java @@ -1,7 +1,10 @@ -package tech.powerjob.server.auth.login; +package tech.powerjob.server.auth.service; +import tech.powerjob.server.auth.LoginContext; import tech.powerjob.server.auth.PowerJobUser; +import tech.powerjob.server.auth.anno.ApiPermission; +import javax.servlet.http.HttpServletRequest; import java.util.Optional; /** @@ -10,7 +13,7 @@ import java.util.Optional; * @author tjq * @since 2023/3/21 */ -public interface PowerJobLoginService { +public interface PowerJobAuthService { /** * 执行真正的登陆操作 @@ -24,5 +27,7 @@ public interface PowerJobLoginService { * @param loginContext 登录上下文 * @return PowerJob 用户 */ - Optional parse(LoginContext loginContext); + Optional parse(HttpServletRequest httpServletRequest); + + boolean hasPermission(PowerJobUser user, ApiPermission apiPermission); } diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/PowerJobLoginServiceImpl.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/PowerJobLoginServiceImpl.java similarity index 79% rename from powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/PowerJobLoginServiceImpl.java rename to powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/PowerJobLoginServiceImpl.java index 5b49a3e6..b3fe2ec3 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/PowerJobLoginServiceImpl.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/PowerJobLoginServiceImpl.java @@ -1,14 +1,16 @@ -package tech.powerjob.server.auth.login; +package tech.powerjob.server.auth.service; import com.google.common.collect.Maps; -import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import tech.powerjob.common.Loggers; +import tech.powerjob.server.auth.LoginContext; import tech.powerjob.server.auth.PowerJobUser; -import tech.powerjob.server.auth.login.biz.BizLoginService; -import tech.powerjob.server.auth.login.biz.BizUser; +import tech.powerjob.server.auth.anno.ApiPermission; +import tech.powerjob.server.auth.login.BizLoginService; +import tech.powerjob.server.auth.login.BizUser; import tech.powerjob.server.persistence.remote.model.UserInfoDO; import tech.powerjob.server.persistence.remote.repository.UserInfoRepository; @@ -23,9 +25,8 @@ import java.util.Optional; * @author tjq * @since 2023/3/21 */ -@Slf4j @Service -public class PowerJobLoginServiceImpl implements PowerJobLoginService { +public class PowerJobLoginServiceImpl implements PowerJobAuthService { private final UserInfoRepository userInfoRepository; private final Map type2LoginService = Maps.newHashMap(); @@ -67,7 +68,7 @@ public class PowerJobLoginServiceImpl implements PowerJobLoginService { // 同步在 PowerJob 用户库创建该用户 UserInfoDO newUser = new UserInfoDO(); newUser.setUsername(dbUserName); - log.info("[PowerJobLoginService] sync user to PowerJobUserSystem: {}", dbUserName); + Loggers.WEB.info("[PowerJobLoginService] sync user to PowerJobUserSystem: {}", dbUserName); userInfoRepository.saveAndFlush(newUser); ret.setUsername(dbUserName); @@ -75,9 +76,9 @@ public class PowerJobLoginServiceImpl implements PowerJobLoginService { } @Override - public Optional parse(LoginContext loginContext) { + public Optional parse(HttpServletRequest httpServletRequest) { - final Optional usernameOpt = parseUsername(loginContext); + final Optional usernameOpt = parseUsername(httpServletRequest); if (!usernameOpt.isPresent()) { return Optional.empty(); } @@ -88,8 +89,12 @@ public class PowerJobLoginServiceImpl implements PowerJobLoginService { }); } - private Optional parseUsername(LoginContext loginContext) { - final HttpServletRequest httpServletRequest = loginContext.getHttpServletRequest(); + @Override + public boolean hasPermission(PowerJobUser user, ApiPermission apiPermission) { + return false; + } + + private Optional parseUsername(HttpServletRequest httpServletRequest) { final String tokenHeader = httpServletRequest.getHeader(TOKEN_HEADER_NAME); if (StringUtils.isEmpty(tokenHeader)) { return Optional.empty();