diff --git a/api/build.gradle b/api/build.gradle index 664a822..9fab618 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -26,6 +26,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jdbc' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-quartz' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.flywaydb:flyway-core' diff --git a/api/src/main/java/com/databasir/api/LoginController.java b/api/src/main/java/com/databasir/api/LoginController.java index 414868d..ad5408a 100644 --- a/api/src/main/java/com/databasir/api/LoginController.java +++ b/api/src/main/java/com/databasir/api/LoginController.java @@ -1,5 +1,6 @@ package com.databasir.api; +import com.databasir.api.config.security.DatabasirUserDetails; import com.databasir.common.DatabasirException; import com.databasir.common.JsonData; import com.databasir.common.exception.InvalidTokenException; @@ -7,10 +8,10 @@ import com.databasir.core.domain.DomainErrors; import com.databasir.core.domain.log.annotation.Operation; import com.databasir.core.domain.login.data.AccessTokenRefreshRequest; import com.databasir.core.domain.login.data.AccessTokenRefreshResponse; +import com.databasir.core.domain.login.data.UserLoginResponse; import com.databasir.core.domain.login.service.LoginService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; @@ -27,8 +28,6 @@ import java.util.Objects; @Slf4j public class LoginController { - private final AuthenticationManager authenticationManager; - private final LoginService loginService; @GetMapping(Routes.Login.LOGOUT) @@ -39,8 +38,8 @@ public class LoginController { } @PostMapping(Routes.Login.REFRESH_ACCESS_TOKEN) - public JsonData refreshAccessTokens(@RequestBody @Valid - AccessTokenRefreshRequest request) { + public JsonData refreshAccessTokens(@RequestBody + @Valid AccessTokenRefreshRequest request) { try { return JsonData.ok(loginService.refreshAccessTokens(request)); } catch (DatabasirException e) { @@ -54,4 +53,13 @@ public class LoginController { } } + @GetMapping(Routes.Login.LOGIN_INFO) + public JsonData getUserLoginData() { + DatabasirUserDetails user = (DatabasirUserDetails) SecurityContextHolder.getContext() + .getAuthentication() + .getPrincipal(); + Integer userId = user.getUserPojo().getId(); + return JsonData.ok(loginService.getUserLoginData(userId)); + } + } diff --git a/api/src/main/java/com/databasir/api/OAuth2AppController.java b/api/src/main/java/com/databasir/api/OAuth2AppController.java new file mode 100644 index 0000000..00d8ab8 --- /dev/null +++ b/api/src/main/java/com/databasir/api/OAuth2AppController.java @@ -0,0 +1,36 @@ +package com.databasir.api; + +import com.databasir.common.JsonData; +import com.databasir.core.infrastructure.oauth2.OAuthAppService; +import com.databasir.core.infrastructure.oauth2.OAuthHandler; +import com.databasir.core.infrastructure.oauth2.data.OAuthAppResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.List; + +@Controller +@RequiredArgsConstructor +public class OAuth2AppController { + + private final OAuthHandler oAuthHandler; + + private final OAuthAppService oAuthAppService; + + @GetMapping("/oauth2/authorization/{registrationId}") + @ResponseBody + public JsonData authorization(@PathVariable String registrationId) { + String authorization = oAuthHandler.authorization(registrationId); + return JsonData.ok(authorization); + } + + @GetMapping("/oauth2/apps") + @ResponseBody + public JsonData> listApps() { + return JsonData.ok(oAuthAppService.listAll()); + } + +} diff --git a/api/src/main/java/com/databasir/api/OAuth2LoginController.java b/api/src/main/java/com/databasir/api/OAuth2LoginController.java deleted file mode 100644 index b5ba38f..0000000 --- a/api/src/main/java/com/databasir/api/OAuth2LoginController.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.databasir.api; - -import com.databasir.common.JsonData; -import com.databasir.core.domain.login.data.LoginKeyResponse; -import com.databasir.core.infrastructure.oauth2.OAuthAppService; -import com.databasir.core.infrastructure.oauth2.OAuthHandler; -import com.databasir.core.infrastructure.oauth2.data.OAuthAppResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.servlet.view.RedirectView; - -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletResponse; -import java.time.ZoneId; -import java.util.List; -import java.util.Map; - -@Controller -@RequiredArgsConstructor -public class OAuth2LoginController { - - private final OAuthHandler oAuthHandler; - - private final OAuthAppService oAuthAppService; - - @GetMapping("/oauth2/authorization/{registrationId}") - public RedirectView authorization(@PathVariable String registrationId) { - String authorization = oAuthHandler.authorization(registrationId); - return new RedirectView(authorization); - } - - @GetMapping("/oauth2/login/{registrationId}") - public RedirectView callback(@PathVariable String registrationId, - @RequestParam Map params, - HttpServletResponse response) { - LoginKeyResponse loginKey = oAuthAppService.oauthCallback(registrationId, params); - // set cookie - Cookie accessTokenCookie = new Cookie("accessToken", loginKey.getAccessToken()); - accessTokenCookie.setPath("/"); - response.addCookie(accessTokenCookie); - - long epochSecond = loginKey.getAccessTokenExpireAt() - .atZone(ZoneId.systemDefault()) - .toInstant() - .toEpochMilli(); - Cookie accessTokenExpireAtCookie = new Cookie("accessTokenExpireAt", epochSecond + ""); - accessTokenExpireAtCookie.setPath("/"); - response.addCookie(accessTokenExpireAtCookie); - return new RedirectView("/"); - } - - @GetMapping("/oauth2/apps") - public JsonData> listApps() { - return JsonData.ok(oAuthAppService.listAll()); - } - -} diff --git a/api/src/main/java/com/databasir/api/Routes.java b/api/src/main/java/com/databasir/api/Routes.java index e6b213f..1aac6c9 100644 --- a/api/src/main/java/com/databasir/api/Routes.java +++ b/api/src/main/java/com/databasir/api/Routes.java @@ -102,6 +102,9 @@ public interface Routes { String LOGOUT = "/logout"; String REFRESH_ACCESS_TOKEN = "/access_tokens"; + + String LOGIN_INFO = "/login_info"; + } interface OperationLog { diff --git a/api/src/main/java/com/databasir/api/config/SecurityConfig.java b/api/src/main/java/com/databasir/api/config/SecurityConfig.java index 55bd7e2..d65279c 100644 --- a/api/src/main/java/com/databasir/api/config/SecurityConfig.java +++ b/api/src/main/java/com/databasir/api/config/SecurityConfig.java @@ -1,6 +1,7 @@ package com.databasir.api.config; import com.databasir.api.Routes; +import com.databasir.api.config.oauth2.DatabasirOauth2LoginFilter; import com.databasir.api.config.security.*; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; diff --git a/api/src/main/java/com/databasir/api/config/oauth2/DatabasirOAuth2Authentication.java b/api/src/main/java/com/databasir/api/config/oauth2/DatabasirOAuth2Authentication.java new file mode 100644 index 0000000..e8f432b --- /dev/null +++ b/api/src/main/java/com/databasir/api/config/oauth2/DatabasirOAuth2Authentication.java @@ -0,0 +1,28 @@ +package com.databasir.api.config.oauth2; + +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.userdetails.UserDetails; + +public class DatabasirOAuth2Authentication extends AbstractAuthenticationToken { + + private Object credentials; + + private Object principal; + + public DatabasirOAuth2Authentication(UserDetails principal) { + super(principal.getAuthorities()); + this.credentials = null; + this.principal = principal; + setAuthenticated(false); + } + + @Override + public Object getCredentials() { + return this.credentials; + } + + @Override + public Object getPrincipal() { + return this.principal; + } +} diff --git a/api/src/main/java/com/databasir/api/config/oauth2/DatabasirOauth2LoginFilter.java b/api/src/main/java/com/databasir/api/config/oauth2/DatabasirOauth2LoginFilter.java new file mode 100644 index 0000000..30fa8b9 --- /dev/null +++ b/api/src/main/java/com/databasir/api/config/oauth2/DatabasirOauth2LoginFilter.java @@ -0,0 +1,55 @@ +package com.databasir.api.config.oauth2; + +import com.databasir.api.config.security.DatabasirUserDetailService; +import com.databasir.core.domain.user.data.UserDetailResponse; +import com.databasir.core.infrastructure.oauth2.OAuthAppService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; +import org.springframework.stereotype.Component; +import org.springframework.util.AntPathMatcher; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Map; + +@Component +@Slf4j +public class DatabasirOauth2LoginFilter extends AbstractAuthenticationProcessingFilter { + + public static final String OAUTH_LOGIN_URI = "/oauth2/login/*"; + + @Autowired + private OAuthAppService oAuthAppService; + + @Autowired + private DatabasirUserDetailService databasirUserDetailService; + + public DatabasirOauth2LoginFilter(AuthenticationManager authenticationManager, + OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler) { + super(OAUTH_LOGIN_URI, authenticationManager); + this.setAuthenticationSuccessHandler(oAuth2AuthenticationSuccessHandler); + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) + throws AuthenticationException, IOException, ServletException { + Map params = request.getParameterMap(); + String registrationId = new AntPathMatcher().extractPathWithinPattern(OAUTH_LOGIN_URI, request.getRequestURI()); + UserDetailResponse userDetailResponse = oAuthAppService.oauthCallback(registrationId, params); + UserDetails details = databasirUserDetailService.loadUserByUsername(userDetailResponse.getUsername()); + DatabasirOAuth2Authentication authentication = new DatabasirOAuth2Authentication(details); + authentication.setAuthenticated(true); + if (log.isDebugEnabled()) { + log.debug("login {} success", registrationId); + } + return authentication; + } + +} diff --git a/api/src/main/java/com/databasir/api/config/oauth2/OAuth2AuthenticationSuccessHandler.java b/api/src/main/java/com/databasir/api/config/oauth2/OAuth2AuthenticationSuccessHandler.java new file mode 100644 index 0000000..05424cb --- /dev/null +++ b/api/src/main/java/com/databasir/api/config/oauth2/OAuth2AuthenticationSuccessHandler.java @@ -0,0 +1,38 @@ +package com.databasir.api.config.oauth2; + +import com.databasir.api.config.security.DatabasirUserDetails; +import com.databasir.common.JsonData; +import com.databasir.core.domain.login.data.LoginKeyResponse; +import com.databasir.core.domain.login.data.UserLoginResponse; +import com.databasir.core.domain.login.service.LoginService; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.CredentialsExpiredException; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component +@RequiredArgsConstructor +public class OAuth2AuthenticationSuccessHandler implements AuthenticationSuccessHandler { + + private final LoginService loginService; + + private final ObjectMapper objectMapper; + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, + HttpServletResponse response, + Authentication authentication) throws IOException, ServletException { + DatabasirUserDetails details = (DatabasirUserDetails) authentication.getPrincipal(); + LoginKeyResponse loginKey = loginService.generate(details.getUserPojo().getId()); + UserLoginResponse data = loginService.getUserLoginData(details.getUserPojo().getId()) + .orElseThrow(() -> new CredentialsExpiredException("请重新登陆")); + objectMapper.writeValue(response.getWriter(), JsonData.ok(data)); + } +} diff --git a/api/src/main/java/com/databasir/api/config/security/DatabasirAuthenticationSuccessHandler.java b/api/src/main/java/com/databasir/api/config/security/DatabasirAuthenticationSuccessHandler.java index 884601f..b9c3ec0 100644 --- a/api/src/main/java/com/databasir/api/config/security/DatabasirAuthenticationSuccessHandler.java +++ b/api/src/main/java/com/databasir/api/config/security/DatabasirAuthenticationSuccessHandler.java @@ -2,11 +2,12 @@ package com.databasir.api.config.security; import com.databasir.common.JsonData; import com.databasir.core.domain.login.data.LoginKeyResponse; +import com.databasir.core.domain.login.data.UserLoginResponse; import com.databasir.core.domain.login.service.LoginService; -import com.databasir.core.domain.user.data.UserLoginResponse; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; +import org.springframework.security.authentication.CredentialsExpiredException; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.stereotype.Component; @@ -16,9 +17,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.time.ZoneId; -import java.util.List; -import java.util.stream.Collectors; @Component @RequiredArgsConstructor @@ -35,28 +33,10 @@ public class DatabasirAuthenticationSuccessHandler implements AuthenticationSucc DatabasirUserDetails user = (DatabasirUserDetails) authentication.getPrincipal(); response.setCharacterEncoding(StandardCharsets.UTF_8.name()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); - UserLoginResponse data = new UserLoginResponse(); - data.setId(user.getUserPojo().getId()); - data.setNickname(user.getUserPojo().getNickname()); - data.setEmail(user.getUserPojo().getEmail()); - data.setUsername(user.getUserPojo().getUsername()); LoginKeyResponse loginKey = loginService.generate(user.getUserPojo().getId()); - data.setAccessToken(loginKey.getAccessToken()); - long expireAt = loginKey.getAccessTokenExpireAt().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); - data.setAccessTokenExpireAt(expireAt); - data.setRefreshToken(loginKey.getRefreshToken()); - - List roles = user.getRoles() - .stream() - .map(ur -> { - UserLoginResponse.RoleResponse roleResponse = new UserLoginResponse.RoleResponse(); - roleResponse.setRole(ur.getRole()); - roleResponse.setGroupId(ur.getGroupId()); - return roleResponse; - }) - .collect(Collectors.toList()); - data.setRoles(roles); + UserLoginResponse data = loginService.getUserLoginData(user.getUserPojo().getId()) + .orElseThrow(() -> new CredentialsExpiredException("请重新登陆")); objectMapper.writeValue(response.getWriter(), JsonData.ok(data)); } } diff --git a/api/src/main/java/com/databasir/api/config/security/DatabasirUserDetailService.java b/api/src/main/java/com/databasir/api/config/security/DatabasirUserDetailService.java index 1780391..6873433 100644 --- a/api/src/main/java/com/databasir/api/config/security/DatabasirUserDetailService.java +++ b/api/src/main/java/com/databasir/api/config/security/DatabasirUserDetailService.java @@ -1,6 +1,5 @@ package com.databasir.api.config.security; -import com.databasir.dao.impl.LoginDao; import com.databasir.dao.impl.UserDao; import com.databasir.dao.impl.UserRoleDao; import com.databasir.dao.tables.pojos.UserPojo; @@ -22,8 +21,6 @@ public class DatabasirUserDetailService implements UserDetailsService { private final UserRoleDao userRoleDao; - private final LoginDao loginDao; - @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { UserPojo user = userDao.selectByEmailOrUsername(username) diff --git a/common/src/main/java/com/databasir/common/JsonData.java b/common/src/main/java/com/databasir/common/JsonData.java index 84d11ed..bc65a88 100644 --- a/common/src/main/java/com/databasir/common/JsonData.java +++ b/common/src/main/java/com/databasir/common/JsonData.java @@ -2,6 +2,8 @@ package com.databasir.common; import lombok.Data; +import java.util.Optional; + @Data public class JsonData { @@ -21,7 +23,7 @@ public class JsonData { private String errMessage; public static JsonData ok() { - return ok(null); + return ok(Optional.empty()); } public static JsonData ok(T data) { @@ -30,6 +32,13 @@ public class JsonData { return jsonData; } + public static JsonData ok(Optional data) { + JsonData jsonData = new JsonData<>(); + jsonData.setData(data.orElse(null)); + return jsonData; + } + + public static JsonData error(String errorCode, String errMessage) { JsonData jsonData = new JsonData<>(); jsonData.setErrCode(errorCode); diff --git a/core/src/main/java/com/databasir/core/domain/user/data/UserLoginResponse.java b/core/src/main/java/com/databasir/core/domain/login/data/UserLoginResponse.java similarity index 86% rename from core/src/main/java/com/databasir/core/domain/user/data/UserLoginResponse.java rename to core/src/main/java/com/databasir/core/domain/login/data/UserLoginResponse.java index a5176a8..33c9556 100644 --- a/core/src/main/java/com/databasir/core/domain/user/data/UserLoginResponse.java +++ b/core/src/main/java/com/databasir/core/domain/login/data/UserLoginResponse.java @@ -1,4 +1,4 @@ -package com.databasir.core.domain.user.data; +package com.databasir.core.domain.login.data; import lombok.Data; @@ -15,6 +15,8 @@ public class UserLoginResponse { private String username; + private String avatar; + private String accessToken; private long accessTokenExpireAt; diff --git a/core/src/main/java/com/databasir/core/domain/login/service/LoginService.java b/core/src/main/java/com/databasir/core/domain/login/service/LoginService.java index f275094..dd108c6 100644 --- a/core/src/main/java/com/databasir/core/domain/login/service/LoginService.java +++ b/core/src/main/java/com/databasir/core/domain/login/service/LoginService.java @@ -4,11 +4,14 @@ import com.databasir.core.domain.DomainErrors; import com.databasir.core.domain.login.data.AccessTokenRefreshRequest; import com.databasir.core.domain.login.data.AccessTokenRefreshResponse; import com.databasir.core.domain.login.data.LoginKeyResponse; +import com.databasir.core.domain.login.data.UserLoginResponse; import com.databasir.core.infrastructure.jwt.JwtTokens; import com.databasir.dao.impl.LoginDao; import com.databasir.dao.impl.UserDao; +import com.databasir.dao.impl.UserRoleDao; import com.databasir.dao.tables.pojos.LoginPojo; import com.databasir.dao.tables.pojos.UserPojo; +import com.databasir.dao.tables.pojos.UserRolePojo; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -16,8 +19,11 @@ import org.springframework.stereotype.Service; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; +import java.util.Collections; +import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -28,6 +34,8 @@ public class LoginService { private final UserDao userDao; + private final UserRoleDao userRoleDao; + private final JwtTokens jwtTokens; public AccessTokenRefreshResponse refreshAccessTokens(AccessTokenRefreshRequest request) { @@ -91,4 +99,34 @@ public class LoginService { .refreshTokenExpireAt(refreshTokenExpireAt) .build(); } + + public Optional getUserLoginData(Integer userId) { + return loginDao.selectByUserId(userId) + .map(login -> { + UserPojo user = userDao.selectById(login.getUserId()); + UserLoginResponse data = new UserLoginResponse(); + data.setId(user.getId()); + data.setNickname(user.getNickname()); + data.setEmail(user.getEmail()); + data.setUsername(user.getUsername()); + data.setAccessToken(login.getAccessToken()); + data.setAvatar(user.getAvatar()); + long expireAt = login.getAccessTokenExpireAt().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + data.setAccessTokenExpireAt(expireAt); + data.setRefreshToken(login.getRefreshToken()); + List rolePojoList = userRoleDao.selectByUserIds(Collections.singletonList(user.getId())); + List roles = rolePojoList + .stream() + .map(ur -> { + UserLoginResponse.RoleResponse roleResponse = new UserLoginResponse.RoleResponse(); + roleResponse.setRole(ur.getRole()); + roleResponse.setGroupId(ur.getGroupId()); + return roleResponse; + }) + .collect(Collectors.toList()); + data.setRoles(roles); + return data; + }); + } + } diff --git a/core/src/main/java/com/databasir/core/domain/user/data/UserLoginRequest.java b/core/src/main/java/com/databasir/core/domain/user/data/UserLoginRequest.java deleted file mode 100644 index 5b5d8a0..0000000 --- a/core/src/main/java/com/databasir/core/domain/user/data/UserLoginRequest.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.databasir.core.domain.user.data; - -import lombok.Data; - -import javax.validation.constraints.NotBlank; - -@Data -public class UserLoginRequest { - - @NotBlank - private String username; - - @NotBlank - private String password; - -} diff --git a/core/src/main/java/com/databasir/core/domain/user/service/UserService.java b/core/src/main/java/com/databasir/core/domain/user/service/UserService.java index a510cd4..dcf680b 100644 --- a/core/src/main/java/com/databasir/core/domain/user/service/UserService.java +++ b/core/src/main/java/com/databasir/core/domain/user/service/UserService.java @@ -91,6 +91,21 @@ public class UserService { return userResponseConverter.detailResponse(pojo, roles, groupNameMapById); } + public Optional get(String email) { + return userDao.selectByEmail(email) + .map(user -> { + List roles = userRoleDao.selectByUserIds(Collections.singletonList(user.getId())); + List groupIds = roles.stream() + .map(UserRolePojo::getGroupId) + .filter(Objects::nonNull) + .collect(toList()); + Map groupNameMapById = groupDao.selectInIds(groupIds) + .stream() + .collect(toMap(GroupPojo::getId, GroupPojo::getName)); + return userResponseConverter.detailResponse(user, roles, groupNameMapById); + }); + } + @Transactional public String renewPassword(Integer userId) { UserPojo userPojo = userDao.selectById(userId); diff --git a/core/src/main/java/com/databasir/core/infrastructure/oauth2/GithubOauthHandler.java b/core/src/main/java/com/databasir/core/infrastructure/oauth2/GithubOauthHandler.java index c9654f8..974618a 100644 --- a/core/src/main/java/com/databasir/core/infrastructure/oauth2/GithubOauthHandler.java +++ b/core/src/main/java/com/databasir/core/infrastructure/oauth2/GithubOauthHandler.java @@ -7,6 +7,8 @@ import com.databasir.dao.impl.OAuthAppDao; import com.databasir.dao.tables.pojos.OauthAppPojo; import com.fasterxml.jackson.databind.JsonNode; import lombok.RequiredArgsConstructor; +import org.jooq.tools.StringUtils; +import org.springframework.security.authentication.CredentialsExpiredException; import org.springframework.stereotype.Component; import org.springframework.web.util.UriComponentsBuilder; @@ -47,17 +49,23 @@ public class GithubOauthHandler implements OAuthHandler { String clientSecret = authApp.getClientSecret(); String baseUrl = authApp.getResourceUrl(); - Map params = context.getCallbackParameters(); - String code = params.get("code"); + Map params = context.getCallbackParameters(); + String code = params.get("code")[0]; String accessToken = githubRemoteService.getToken(baseUrl, clientId, clientSecret, code) .get("access_token") .asText(); + if (StringUtils.isBlank(accessToken)) { + throw new CredentialsExpiredException("授权失效,请重新登陆"); + } String email = null; for (JsonNode node : githubRemoteService.getEmail(baseUrl, accessToken)) { if (node.get("primary").asBoolean()) { email = node.get("email").asText(); } } + if (StringUtils.isBlank(email)) { + throw new CredentialsExpiredException("授权失效,请重新登陆"); + } JsonNode profile = githubRemoteService.getProfile(baseUrl, accessToken); String nickname = profile.get("name").asText(); String avatar = profile.get("avatar_url").asText(); diff --git a/core/src/main/java/com/databasir/core/infrastructure/oauth2/OAuthAppService.java b/core/src/main/java/com/databasir/core/infrastructure/oauth2/OAuthAppService.java index 3955e73..6796723 100644 --- a/core/src/main/java/com/databasir/core/infrastructure/oauth2/OAuthAppService.java +++ b/core/src/main/java/com/databasir/core/infrastructure/oauth2/OAuthAppService.java @@ -1,18 +1,20 @@ package com.databasir.core.infrastructure.oauth2; -import com.databasir.core.domain.login.data.LoginKeyResponse; -import com.databasir.core.domain.login.service.LoginService; import com.databasir.core.domain.user.data.UserCreateRequest; +import com.databasir.core.domain.user.data.UserDetailResponse; import com.databasir.core.domain.user.service.UserService; +import com.databasir.core.infrastructure.oauth2.converter.OAuthAppResponseConverter; import com.databasir.core.infrastructure.oauth2.data.OAuthAppResponse; import com.databasir.dao.impl.OAuthAppDao; import com.databasir.dao.tables.pojos.OauthAppPojo; import lombok.RequiredArgsConstructor; +import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -24,16 +26,16 @@ public class OAuthAppService { private final UserService userService; - private final LoginService loginService; + private final OAuthAppResponseConverter oAuthAppResponseConverter; - public LoginKeyResponse oauthCallback(String registrationId, Map params) { + public UserDetailResponse oauthCallback(String registrationId, Map params) { // match handler OauthAppPojo app = oAuthAppDao.selectByRegistrationId(registrationId); OAuthHandler oAuthHandler = oAuthHandlers.stream() .filter(handler -> handler.support(app.getAppType())) .findFirst() - .orElseThrow(); + .orElseThrow(() -> new UsernameNotFoundException("暂不支持该类型登陆")); // process by handler OAuthProcessContext context = OAuthProcessContext.builder() @@ -42,20 +44,25 @@ public class OAuthAppService { .build(); OAuthProcessResult result = oAuthHandler.process(context); - // create new user - UserCreateRequest user = new UserCreateRequest(); - user.setUsername(result.getUsername()); - user.setNickname(result.getNickname()); - user.setEmail(result.getEmail()); - user.setAvatar(result.getAvatar()); - user.setPassword(UUID.randomUUID().toString().substring(0, 6)); - Integer userId = userService.create(user); - - return loginService.generate(userId); + // get or create new user + return userService.get(result.getEmail()) + .orElseGet(() -> { + UserCreateRequest user = new UserCreateRequest(); + user.setUsername(result.getUsername()); + user.setNickname(result.getNickname()); + user.setEmail(result.getEmail()); + user.setAvatar(result.getAvatar()); + user.setPassword(UUID.randomUUID().toString().substring(0, 6)); + Integer id = userService.create(user); + return userService.get(id); + }); } public List listAll() { - return null; + List apps = oAuthAppDao.selectAll(); + return apps.stream() + .map(oAuthAppResponseConverter::to) + .collect(Collectors.toList()); } } diff --git a/core/src/main/java/com/databasir/core/infrastructure/oauth2/OAuthProcessContext.java b/core/src/main/java/com/databasir/core/infrastructure/oauth2/OAuthProcessContext.java index 36e2de6..a48ae35 100644 --- a/core/src/main/java/com/databasir/core/infrastructure/oauth2/OAuthProcessContext.java +++ b/core/src/main/java/com/databasir/core/infrastructure/oauth2/OAuthProcessContext.java @@ -17,6 +17,6 @@ public class OAuthProcessContext { private String registrationId; @Builder.Default - private Map callbackParameters = new HashMap<>(); + private Map callbackParameters = new HashMap<>(); } diff --git a/core/src/main/java/com/databasir/core/infrastructure/oauth2/converter/OAuthAppResponseConverter.java b/core/src/main/java/com/databasir/core/infrastructure/oauth2/converter/OAuthAppResponseConverter.java new file mode 100644 index 0000000..e1114b6 --- /dev/null +++ b/core/src/main/java/com/databasir/core/infrastructure/oauth2/converter/OAuthAppResponseConverter.java @@ -0,0 +1,11 @@ +package com.databasir.core.infrastructure.oauth2.converter; + +import com.databasir.core.infrastructure.oauth2.data.OAuthAppResponse; +import com.databasir.dao.tables.pojos.OauthAppPojo; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface OAuthAppResponseConverter { + + OAuthAppResponse to(OauthAppPojo pojo); +} diff --git a/core/src/main/java/com/databasir/core/infrastructure/oauth2/data/OAuthAppResponse.java b/core/src/main/java/com/databasir/core/infrastructure/oauth2/data/OAuthAppResponse.java index a23f31a..d6579a1 100644 --- a/core/src/main/java/com/databasir/core/infrastructure/oauth2/data/OAuthAppResponse.java +++ b/core/src/main/java/com/databasir/core/infrastructure/oauth2/data/OAuthAppResponse.java @@ -15,18 +15,14 @@ public class OAuthAppResponse { private Integer id; - private String name; + private String appName; - private String icon; + private String appIcon; + + private String appType; private String registrationId; - private String clientId; - - private String clientSecret; - - private LocalDateTime updateAt; - private LocalDateTime createAt; } diff --git a/dao/src/main/java/com/databasir/dao/impl/BaseDao.java b/dao/src/main/java/com/databasir/dao/impl/BaseDao.java index 872ab8c..0ab42b7 100644 --- a/dao/src/main/java/com/databasir/dao/impl/BaseDao.java +++ b/dao/src/main/java/com/databasir/dao/impl/BaseDao.java @@ -90,6 +90,12 @@ public abstract class BaseDao { .fetchInto(pojoType); } + public List selectAll() { + return this.getDslContext() + .selectFrom(table()) + .fetchInto(pojoType); + } + public Page selectByPage(Pageable request, Condition condition) { Integer count = getDslContext() .selectCount().from(table).where(condition) diff --git a/dao/src/main/java/com/databasir/dao/impl/LoginDao.java b/dao/src/main/java/com/databasir/dao/impl/LoginDao.java index fc5f6ed..cd5adbb 100644 --- a/dao/src/main/java/com/databasir/dao/impl/LoginDao.java +++ b/dao/src/main/java/com/databasir/dao/impl/LoginDao.java @@ -40,6 +40,13 @@ public class LoginDao extends BaseDao { .fetchOptionalInto(LoginPojo.class); } + public Optional selectByAccessToken(String accessToken) { + return getDslContext() + .selectFrom(LOGIN).where(LOGIN.ACCESS_TOKEN.eq(accessToken) + .and(LOGIN.ACCESS_TOKEN_EXPIRE_AT.ge(LocalDateTime.now()))) + .fetchOptionalInto(LoginPojo.class); + } + public void updateAccessToken(String accessToken, LocalDateTime accessTokenExpireAt, Integer userId) { getDslContext() .update(LOGIN)