From d789d681805a5bd9cde2272c6c44ada3d5b6f327 Mon Sep 17 00:00:00 2001 From: tjq Date: Sun, 16 Apr 2023 16:09:39 +0800 Subject: [PATCH] feat: [auth] add controller and loginUserHolder api --- .../powerjob/server/auth/LoginUserHolder.java | 24 ++++++ .../interceptor/PowerJobAuthInterceptor.java | 10 +++ .../server/auth/jwt/impl/JwtServiceImpl.java | 10 +-- .../login/impl/DingTalkBizLoginService.java | 6 +- ...ice.java => PowerJobSelfLoginService.java} | 4 +- .../auth/service/PowerJobAuthService.java | 3 + .../auth/service/PowerJobAuthServiceImpl.java | 32 +++++-- .../server/web/controller/AuthController.java | 84 +++++++++++++++++++ .../web/controller/UserInfoController.java | 48 ----------- 9 files changed, 154 insertions(+), 67 deletions(-) create mode 100644 powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/LoginUserHolder.java rename powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/{DefaultBizLoginService.java => PowerJobSelfLoginService.java} (96%) create mode 100644 powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/AuthController.java diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/LoginUserHolder.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/LoginUserHolder.java new file mode 100644 index 00000000..1dd693c5 --- /dev/null +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/LoginUserHolder.java @@ -0,0 +1,24 @@ +package tech.powerjob.server.auth; + +/** + * LoginUserHolder + * + * @author tjq + * @since 2023/4/16 + */ +public class LoginUserHolder { + + private static final ThreadLocal TL = new ThreadLocal<>(); + + public static PowerJobUser get() { + return TL.get(); + } + + public static void set(PowerJobUser powerJobUser) { + TL.set(powerJobUser); + } + + public static void clean() { + TL.remove(); + } +} 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 index 7db6af57..69e5ac94 100644 --- 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 @@ -7,6 +7,7 @@ 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.LoginUserHolder; import tech.powerjob.server.auth.PowerJobUser; import tech.powerjob.server.auth.anno.ApiPermission; import tech.powerjob.server.auth.service.PowerJobAuthService; @@ -54,6 +55,10 @@ public class PowerJobAuthInterceptor implements HandlerInterceptor { // 登陆用户进行权限校验 final PowerJobUser powerJobUser = loginUserOpt.get(); + + // 写入上下文 + LoginUserHolder.set(powerJobUser); + final boolean hasPermission = powerJobAuthService.hasPermission(request, powerJobUser, apiPermissionAnno); if (hasPermission) { return true; @@ -65,6 +70,11 @@ public class PowerJobAuthInterceptor implements HandlerInterceptor { throw new PowerJobException("Permission denied!"); } + @Override + public void afterCompletion(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler, Exception ex) throws Exception { + LoginUserHolder.clean(); + } + private static String parseResourceName(ApiPermission apiPermission, HandlerMethod handlerMethod) { final String name = apiPermission.name(); if (StringUtils.isNotEmpty(name)) { diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/jwt/impl/JwtServiceImpl.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/jwt/impl/JwtServiceImpl.java index 43864451..c344276a 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/jwt/impl/JwtServiceImpl.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/jwt/impl/JwtServiceImpl.java @@ -37,10 +37,10 @@ public class JwtServiceImpl implements JwtService { * GoodSong */ private static final String BASE_SECURITY = - "CengMengXiangZhangJianZouTianYa" + - "KanYiKanShiJieDeFanHua" + - "NianShaoDeXinZongYouXieQingKuang" + - "RuJinWoSiHaiWeiJia" + "死去元知万事空" + + "但悲不见九州同" + + "王师北定中原日" + + "家祭无忘告乃翁" ; @Override @@ -72,7 +72,7 @@ public class JwtServiceImpl implements JwtService { .build() .parseClaimsJws(jwtStr); Map ret = Maps.newHashMap(); - claimsJws.getBody().forEach(ret::put); + ret.putAll(claimsJws.getBody()); return ret; } diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/DingTalkBizLoginService.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/DingTalkBizLoginService.java index 463ace80..d8ff7752 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/DingTalkBizLoginService.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/DingTalkBizLoginService.java @@ -50,11 +50,11 @@ public class DingTalkBizLoginService implements BizLoginService { @Value("${oms.auth.dingtalk.callbackUrl}") private String dingTalkCallbackUrl; - private static final String DEFAULT_LOGIN_SERVICE = "DingTalk"; + private static final String DING_TALK = "DingTalk"; @Override public String type() { - return DEFAULT_LOGIN_SERVICE; + return DING_TALK; } @Override @@ -71,7 +71,7 @@ public class DingTalkBizLoginService implements BizLoginService { "&response_type=code" + "&client_id=" + dingTalkAppKey + "&scope=openid" + - "&state=DingTalk" + + "&state=" + DING_TALK + "&prompt=consent"; Loggers.WEB.info("[DingTalkBizLoginService] login url: {}", url); return url; diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/DefaultBizLoginService.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/PowerJobSelfLoginService.java similarity index 96% rename from powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/DefaultBizLoginService.java rename to powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/PowerJobSelfLoginService.java index d35c6fff..c04c8bc5 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/DefaultBizLoginService.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/PowerJobSelfLoginService.java @@ -23,7 +23,7 @@ import java.util.Optional; * @since 2023/3/20 */ @Service -public class DefaultBizLoginService implements BizLoginService { +public class PowerJobSelfLoginService implements BizLoginService { @Resource private UserInfoRepository userInfoRepository; @@ -40,7 +40,7 @@ public class DefaultBizLoginService implements BizLoginService { @Override public String loginUrl() { - return "forward:/user/loginCallback"; + return null; } @Override diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/PowerJobAuthService.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/PowerJobAuthService.java index d0f80f67..5eab9876 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/PowerJobAuthService.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/PowerJobAuthService.java @@ -5,6 +5,7 @@ import tech.powerjob.server.auth.PowerJobUser; import tech.powerjob.server.auth.anno.ApiPermission; import javax.servlet.http.HttpServletRequest; +import java.util.List; import java.util.Optional; /** @@ -15,6 +16,8 @@ import java.util.Optional; */ public interface PowerJobAuthService { + List supportTypes(); + /** * 开始登陆 * @param loginContext 请求 diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/PowerJobAuthServiceImpl.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/PowerJobAuthServiceImpl.java index 82ebc603..6f2b97a8 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/PowerJobAuthServiceImpl.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/PowerJobAuthServiceImpl.java @@ -1,6 +1,7 @@ package tech.powerjob.server.auth.service; import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import org.apache.commons.lang3.StringUtils; @@ -39,7 +40,7 @@ public class PowerJobAuthServiceImpl implements PowerJobAuthService { private final UserRoleRepository userRoleRepository; private final Map type2LoginService = Maps.newHashMap(); - private static final String JWT_NAME = "powerjob_token"; + private static final String JWT_NAME = "power_jwt"; private static final String KEY_USERID = "userId"; @@ -52,6 +53,11 @@ public class PowerJobAuthServiceImpl implements PowerJobAuthService { } + @Override + public List supportTypes() { + return Lists.newArrayList(type2LoginService.keySet()); + } + @Override public String startLogin(LoginContext loginContext) { final BizLoginService loginService = fetchBizLoginService(loginContext); @@ -68,20 +74,20 @@ public class PowerJobAuthServiceImpl implements PowerJobAuthService { final Optional powerJobUserOpt = userInfoRepository.findByUsername(dbUserName); PowerJobUser ret = new PowerJobUser(); - // 存在则响应 PowerJob 用户 + // 存在则响应 PowerJob 用户,否则同步在 PowerJob 用户库创建该用户 if (powerJobUserOpt.isPresent()) { final UserInfoDO dbUser = powerJobUserOpt.get(); BeanUtils.copyProperties(dbUser, ret); ret.setUsername(dbUserName); - return ret; + } else { + UserInfoDO newUser = new UserInfoDO(); + newUser.setUsername(dbUserName); + Loggers.WEB.info("[PowerJobLoginService] sync user to PowerJobUserSystem: {}", dbUserName); + userInfoRepository.saveAndFlush(newUser); + ret.setUsername(dbUserName); } - // 同步在 PowerJob 用户库创建该用户 - UserInfoDO newUser = new UserInfoDO(); - newUser.setUsername(dbUserName); - Loggers.WEB.info("[PowerJobLoginService] sync user to PowerJobUserSystem: {}", dbUserName); - userInfoRepository.saveAndFlush(newUser); - ret.setUsername(dbUserName); + fillJwt(ret); return ret; } @@ -164,4 +170,12 @@ public class PowerJobAuthServiceImpl implements PowerJobAuthService { } return loginService; } + + private void fillJwt(PowerJobUser powerJobUser) { + Map jwtMap = Maps.newHashMap(); + + jwtMap.put(KEY_USERID, powerJobUser.getId()); + + powerJobUser.setJwtToken(jwtService.build(jwtMap)); + } } diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/AuthController.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/AuthController.java new file mode 100644 index 00000000..49a240bb --- /dev/null +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/AuthController.java @@ -0,0 +1,84 @@ +package tech.powerjob.server.web.controller; + +import lombok.SneakyThrows; +import org.springframework.web.bind.annotation.*; +import tech.powerjob.common.response.ResultDTO; +import tech.powerjob.server.auth.LoginContext; +import tech.powerjob.server.auth.PowerJobUser; +import tech.powerjob.server.auth.service.PowerJobAuthService; +import tech.powerjob.server.web.request.UserLoginRequest; + +import javax.annotation.Resource; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 登录 & 权限相关 + * + * @author tjq + * @since 2023/4/16 + */ +@RestController +@RequestMapping("/auth") +public class AuthController { + + @Resource + private PowerJobAuthService powerJobAuthService; + + @GetMapping("/supportTypes") + public ResultDTO> options() { + return ResultDTO.success(powerJobAuthService.supportTypes()); + } + + @SneakyThrows + @GetMapping("/startLogin") + public String tryLogin(UserLoginRequest loginRequest, HttpServletRequest request, HttpServletResponse response) { + + LoginContext loginContext = new LoginContext() + .setLoginType(loginRequest.getType()) + .setLoginInfo(loginRequest.getLoginInfo()) + .setHttpServletRequest(request); + + final String realLoginUrl = powerJobAuthService.startLogin(loginContext); + // 统一重定向 + response.sendRedirect(realLoginUrl); + return null; + } + + @PostMapping("/selfLogin") + public ResultDTO selfLogin(LoginContext loginContext, HttpServletResponse httpServletResponse) { + try { + final PowerJobUser powerJobUser = powerJobAuthService.tryLogin(loginContext); + if (powerJobUser == null) { + return ResultDTO.failed("USER_NOT_FOUND"); + } + httpServletResponse.addCookie(new Cookie("power_jwt", powerJobUser.getJwtToken())); + return ResultDTO.success(powerJobUser); + } catch (Exception e) { + return ResultDTO.failed(e.getMessage()); + } + } + + /** + * 第三方账号体系回调登录接口 + * @param httpServletRequest 请求 + * @param httpServletResponse 响应 + * @return 登录结果 + */ + @RequestMapping(value = "/loginCallback", method = {RequestMethod.GET, RequestMethod.POST}) + public ResultDTO loginCallback(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { + LoginContext loginContext = new LoginContext() + .setHttpServletRequest(httpServletRequest); + + // 常见登录组件的标准规范(钉钉、企业微信、飞书),第三方原样透传 + final String state = httpServletRequest.getParameter("state"); + loginContext.setLoginType(state); + + final PowerJobUser powerJobUser = powerJobAuthService.tryLogin(loginContext); + + httpServletResponse.addCookie(new Cookie("power_jwt", powerJobUser.getJwtToken())); + return ResultDTO.success(powerJobUser); + } +} diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/UserInfoController.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/UserInfoController.java index 4bd4b4a1..2db5da8b 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/UserInfoController.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/UserInfoController.java @@ -4,25 +4,17 @@ import com.google.common.collect.Lists; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.*; import tech.powerjob.common.response.ResultDTO; -import tech.powerjob.server.auth.LoginContext; -import tech.powerjob.server.auth.PowerJobUser; -import tech.powerjob.server.auth.service.PowerJobAuthService; import tech.powerjob.server.core.service.UserService; import tech.powerjob.server.persistence.remote.model.UserInfoDO; import tech.powerjob.server.persistence.remote.repository.UserInfoRepository; import tech.powerjob.server.web.request.ModifyUserInfoRequest; -import tech.powerjob.server.web.request.UserLoginRequest; import javax.annotation.Resource; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.util.List; import java.util.stream.Collectors; @@ -39,46 +31,6 @@ public class UserInfoController { private UserService userService; @Resource private UserInfoRepository userInfoRepository; - @Resource - private PowerJobAuthService powerJobAuthService; - - - @SneakyThrows - @GetMapping("/startLogin") - public String tryLogin(UserLoginRequest loginRequest, HttpServletRequest request, HttpServletResponse response) { - - LoginContext loginContext = new LoginContext() - .setLoginType(loginRequest.getType()) - .setLoginInfo(loginRequest.getLoginInfo()) - .setHttpServletRequest(request); - - final String realLoginUrl = powerJobAuthService.startLogin(loginContext); - // 统一重定向 - response.sendRedirect(realLoginUrl); - return null; - } - - @RequestMapping(value = "/loginCallback", method = {RequestMethod.GET, RequestMethod.POST}) - public ResultDTO loginCallback(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { - LoginContext loginContext = new LoginContext() - .setHttpServletRequest(httpServletRequest); - - // 尝试读取 body - if (RequestMethod.POST.name().equalsIgnoreCase(httpServletRequest.getMethod())) { - // TODO: 从 post 读取 body - } - - // 钉钉回调 - final String state = httpServletRequest.getParameter("state"); - if ("DingTalk".equalsIgnoreCase(state)) { - loginContext.setLoginType("DingTalk"); - } - - final PowerJobUser powerJobUser = powerJobAuthService.tryLogin(loginContext); - - httpServletResponse.addCookie(new Cookie("powerjob_token", powerJobUser.getJwtToken())); - return ResultDTO.success(powerJobUser); - } @PostMapping("save") public ResultDTO save(@RequestBody ModifyUserInfoRequest request) {