feat: PowerJobAuthInterceptor

This commit is contained in:
tjq 2023-03-25 13:38:43 +08:00
parent 926c8758ed
commit ce6c62f786
9 changed files with 145 additions and 27 deletions

View File

@ -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");
}

View File

@ -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;

View File

@ -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 名称

View File

@ -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<PowerJobUser> 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";
}
}

View File

@ -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;

View File

@ -1,4 +1,4 @@
package tech.powerjob.server.auth.login.biz;
package tech.powerjob.server.auth.login;
import lombok.Getter;
import lombok.Setter;

View File

@ -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<UserInfoDO> 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();
}

View File

@ -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<PowerJobUser> parse(LoginContext loginContext);
Optional<PowerJobUser> parse(HttpServletRequest httpServletRequest);
boolean hasPermission(PowerJobUser user, ApiPermission apiPermission);
}

View File

@ -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<String, BizLoginService> 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<PowerJobUser> parse(LoginContext loginContext) {
public Optional<PowerJobUser> parse(HttpServletRequest httpServletRequest) {
final Optional<String> usernameOpt = parseUsername(loginContext);
final Optional<String> usernameOpt = parseUsername(httpServletRequest);
if (!usernameOpt.isPresent()) {
return Optional.empty();
}
@ -88,8 +89,12 @@ public class PowerJobLoginServiceImpl implements PowerJobLoginService {
});
}
private Optional<String> parseUsername(LoginContext loginContext) {
final HttpServletRequest httpServletRequest = loginContext.getHttpServletRequest();
@Override
public boolean hasPermission(PowerJobUser user, ApiPermission apiPermission) {
return false;
}
private Optional<String> parseUsername(HttpServletRequest httpServletRequest) {
final String tokenHeader = httpServletRequest.getHeader(TOKEN_HEADER_NAME);
if (StringUtils.isEmpty(tokenHeader)) {
return Optional.empty();