mirror of
https://github.com/vran-dev/databasir.git
synced 2025-08-09 02:20:51 +08:00
Feature/oauth2 github (#27)
* feat: jooq generate * feat: support github oauth * feat: support oauth2 login * feat: update content-type * feat: add custom authentication exception * feat: add oauth2 app api * fix: checkstyle
This commit is contained in:
@@ -10,6 +10,7 @@ import lombok.RequiredArgsConstructor;
|
||||
public enum DomainErrors implements DatabasirErrors {
|
||||
REFRESH_TOKEN_EXPIRED("X_0001", "refresh token expired"),
|
||||
INVALID_REFRESH_TOKEN_OPERATION("X_0002", "invalid refresh token operation"),
|
||||
NETWORK_ERROR("X_0003", "网络似乎不稳定,请稍后再试"),
|
||||
|
||||
NOT_SUPPORT_DATABASE_TYPE("A_10000", "不支持的数据库类型, 请检查项目配置"),
|
||||
PROJECT_NOT_FOUND("A_10001", "项目不存在"),
|
||||
@@ -23,7 +24,8 @@ public enum DomainErrors implements DatabasirErrors {
|
||||
CANNOT_UPDATE_SELF_ROLE("A_10009", "无法对自己执行角色变更的操作"),
|
||||
UPDATE_PASSWORD_CONFIRM_FAILED("A_10010", "两次密码输入不一致"),
|
||||
ORIGIN_PASSWORD_NOT_CORRECT("A_10011", "原密码不正确"),
|
||||
INVALID_CRON_EXPRESSION("A_10012", "不合法的 cron 表达式");
|
||||
INVALID_CRON_EXPRESSION("A_10012", "不合法的 cron 表达式"),
|
||||
REGISTRATION_ID_DUPLICATE("A_10013", "应用注册 ID 不能重复");
|
||||
|
||||
private final String errCode;
|
||||
|
||||
|
@@ -0,0 +1,111 @@
|
||||
package com.databasir.core.domain.app;
|
||||
|
||||
import com.databasir.core.domain.DomainErrors;
|
||||
import com.databasir.core.domain.app.converter.OAuthAppPojoConverter;
|
||||
import com.databasir.core.domain.app.converter.OAuthAppResponseConverter;
|
||||
import com.databasir.core.domain.app.data.*;
|
||||
import com.databasir.core.domain.app.handler.OpenAuthHandler;
|
||||
import com.databasir.core.domain.app.handler.OAuthProcessContext;
|
||||
import com.databasir.core.domain.app.handler.OAuthProcessResult;
|
||||
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.dao.impl.OauthAppDao;
|
||||
import com.databasir.dao.tables.pojos.OauthAppPojo;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class OpenAuthAppService {
|
||||
|
||||
private final List<OpenAuthHandler> openAuthHandlers;
|
||||
|
||||
private final OauthAppDao oauthAppDao;
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
private final OAuthAppResponseConverter oauthAppResponseConverter;
|
||||
|
||||
private final OAuthAppPojoConverter oauthAppPojoConverter;
|
||||
|
||||
public UserDetailResponse oauthCallback(String registrationId, Map<String, String[]> params) {
|
||||
|
||||
// match handler
|
||||
OauthAppPojo app = oauthAppDao.selectByRegistrationId(registrationId);
|
||||
OpenAuthHandler openAuthHandler = openAuthHandlers.stream()
|
||||
.filter(handler -> handler.support(app.getAppType()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new UsernameNotFoundException("暂不支持该类型登陆"));
|
||||
|
||||
// process by handler
|
||||
OAuthProcessContext context = OAuthProcessContext.builder()
|
||||
.callbackParameters(params)
|
||||
.registrationId(registrationId)
|
||||
.build();
|
||||
OAuthProcessResult result = openAuthHandler.process(context);
|
||||
|
||||
// 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.setEnabled(true);
|
||||
user.setPassword(UUID.randomUUID().toString().substring(0, 6));
|
||||
Integer id = userService.create(user);
|
||||
return userService.get(id);
|
||||
});
|
||||
}
|
||||
|
||||
public List<OAuthAppResponse> listAll() {
|
||||
List<OauthAppPojo> apps = oauthAppDao.selectAll();
|
||||
return apps.stream()
|
||||
.map(oauthAppResponseConverter::to)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public void deleteById(Integer id) {
|
||||
if (oauthAppDao.existsById(id)) {
|
||||
oauthAppDao.deleteById(id);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateById(OAuthAppUpdateRequest request) {
|
||||
OauthAppPojo pojo = oauthAppPojoConverter.of(request);
|
||||
try {
|
||||
oauthAppDao.updateById(pojo);
|
||||
} catch (DuplicateKeyException e) {
|
||||
throw DomainErrors.REGISTRATION_ID_DUPLICATE.exception();
|
||||
}
|
||||
}
|
||||
|
||||
public Integer create(OAuthAppCreateRequest request) {
|
||||
OauthAppPojo pojo = oauthAppPojoConverter.of(request);
|
||||
try {
|
||||
return oauthAppDao.insertAndReturnId(pojo);
|
||||
} catch (DuplicateKeyException e) {
|
||||
throw DomainErrors.REGISTRATION_ID_DUPLICATE.exception();
|
||||
}
|
||||
}
|
||||
|
||||
public Page<OAuthAppPageResponse> listPage(Pageable page, OAuthAppPageCondition condition) {
|
||||
return oauthAppDao.selectByPage(page, condition.toCondition()).map(oauthAppPojoConverter::toPageResponse);
|
||||
}
|
||||
|
||||
public Optional<OAuthAppDetailResponse> getOne(Integer id) {
|
||||
return oauthAppDao.selectOptionalById(id).map(oauthAppPojoConverter::toDetailResponse);
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
package com.databasir.core.domain.app.converter;
|
||||
|
||||
import com.databasir.core.domain.app.data.OAuthAppCreateRequest;
|
||||
import com.databasir.core.domain.app.data.OAuthAppDetailResponse;
|
||||
import com.databasir.core.domain.app.data.OAuthAppPageResponse;
|
||||
import com.databasir.core.domain.app.data.OAuthAppUpdateRequest;
|
||||
import com.databasir.dao.tables.pojos.OauthAppPojo;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface OAuthAppPojoConverter {
|
||||
|
||||
@Mapping(target = "id", ignore = true)
|
||||
@Mapping(target = "createAt", ignore = true)
|
||||
@Mapping(target = "updateAt", ignore = true)
|
||||
OauthAppPojo of(OAuthAppCreateRequest request);
|
||||
|
||||
@Mapping(target = "createAt", ignore = true)
|
||||
@Mapping(target = "updateAt", ignore = true)
|
||||
OauthAppPojo of(OAuthAppUpdateRequest request);
|
||||
|
||||
OAuthAppPageResponse toPageResponse(OauthAppPojo pojo);
|
||||
|
||||
OAuthAppDetailResponse toDetailResponse(OauthAppPojo pojo);
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package com.databasir.core.domain.app.converter;
|
||||
|
||||
import com.databasir.core.domain.app.data.OAuthAppResponse;
|
||||
import com.databasir.dao.tables.pojos.OauthAppPojo;
|
||||
import org.mapstruct.Mapper;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface OAuthAppResponseConverter {
|
||||
|
||||
OAuthAppResponse to(OauthAppPojo pojo);
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
package com.databasir.core.domain.app.data;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
public class OAuthAppCreateRequest {
|
||||
|
||||
@NotNull
|
||||
private String registrationId;
|
||||
|
||||
@NotBlank
|
||||
private String appName;
|
||||
|
||||
@NotBlank
|
||||
private String appType;
|
||||
|
||||
private String appIcon;
|
||||
|
||||
@NotBlank
|
||||
private String authUrl;
|
||||
|
||||
@NotBlank
|
||||
private String resourceUrl;
|
||||
|
||||
@NotBlank
|
||||
private String clientId;
|
||||
|
||||
@NotBlank
|
||||
private String clientSecret;
|
||||
|
||||
private String scope;
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package com.databasir.core.domain.app.data;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class OAuthAppDetailResponse {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private String appName;
|
||||
|
||||
private String appIcon;
|
||||
|
||||
private String appType;
|
||||
|
||||
private String registrationId;
|
||||
|
||||
private String clientId;
|
||||
|
||||
private String clientSecret;
|
||||
|
||||
private String authUrl;
|
||||
|
||||
private String resourceUrl;
|
||||
|
||||
private LocalDateTime updateAt;
|
||||
|
||||
private LocalDateTime createAt;
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package com.databasir.core.domain.app.data;
|
||||
|
||||
import com.databasir.dao.Tables;
|
||||
import lombok.Data;
|
||||
import org.jooq.Condition;
|
||||
import org.jooq.impl.DSL;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class OAuthAppPageCondition {
|
||||
|
||||
private String appNameContains;
|
||||
|
||||
private String appType;
|
||||
|
||||
public Condition toCondition() {
|
||||
List<Condition> conditions = new ArrayList<>();
|
||||
if (appNameContains != null && !appNameContains.trim().equals("")) {
|
||||
conditions.add(Tables.OAUTH_APP.APP_NAME.contains(appNameContains));
|
||||
}
|
||||
if (appType != null) {
|
||||
conditions.add(Tables.OAUTH_APP.APP_TYPE.eq(appType));
|
||||
}
|
||||
return conditions.stream()
|
||||
.reduce(Condition::and)
|
||||
.orElse(DSL.trueCondition());
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package com.databasir.core.domain.app.data;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class OAuthAppPageResponse {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private String appName;
|
||||
|
||||
private String appIcon;
|
||||
|
||||
private String appType;
|
||||
|
||||
private String registrationId;
|
||||
|
||||
private String clientId;
|
||||
|
||||
private String authUrl;
|
||||
|
||||
private String resourceUrl;
|
||||
|
||||
private LocalDateTime updateAt;
|
||||
|
||||
private LocalDateTime createAt;
|
||||
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
package com.databasir.core.domain.app.data;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class OAuthAppResponse {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private String appName;
|
||||
|
||||
private String appIcon;
|
||||
|
||||
private String appType;
|
||||
|
||||
private String registrationId;
|
||||
|
||||
private LocalDateTime createAt;
|
||||
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
package com.databasir.core.domain.app.data;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
public class OAuthAppUpdateRequest {
|
||||
|
||||
@NotNull
|
||||
private Integer id;
|
||||
|
||||
@NotBlank
|
||||
private String registrationId;
|
||||
|
||||
@NotBlank
|
||||
private String appName;
|
||||
|
||||
@NotBlank
|
||||
private String appType;
|
||||
|
||||
private String appIcon;
|
||||
|
||||
@NotBlank
|
||||
private String authUrl;
|
||||
|
||||
@NotBlank
|
||||
private String resourceUrl;
|
||||
|
||||
@NotBlank
|
||||
private String clientId;
|
||||
|
||||
@NotBlank
|
||||
private String clientSecret;
|
||||
|
||||
private String scope;
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package com.databasir.core.domain.app.exception;
|
||||
|
||||
import com.databasir.common.DatabasirException;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
public class DatabasirAuthenticationException extends AuthenticationException {
|
||||
|
||||
public DatabasirAuthenticationException(DatabasirException databasirException) {
|
||||
super(databasirException.getErrMessage(), databasirException);
|
||||
}
|
||||
|
||||
public DatabasirAuthenticationException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public DatabasirAuthenticationException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
package com.databasir.core.domain.app.handler;
|
||||
|
||||
import com.databasir.core.domain.DomainErrors;
|
||||
import com.databasir.core.domain.app.exception.DatabasirAuthenticationException;
|
||||
import com.databasir.core.infrastructure.remote.github.GithubRemoteService;
|
||||
import com.databasir.dao.enums.OAuthAppType;
|
||||
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;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class GithubOpenAuthHandler implements OpenAuthHandler {
|
||||
|
||||
private final GithubRemoteService githubRemoteService;
|
||||
|
||||
private final OauthAppDao oauthAppDao;
|
||||
|
||||
@Override
|
||||
public boolean support(String oauthAppType) {
|
||||
return OAuthAppType.GITHUB.isSame(oauthAppType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String authorization(String registrationId) {
|
||||
OauthAppPojo app = oauthAppDao.selectByRegistrationId(registrationId);
|
||||
String authUrl = app.getAuthUrl();
|
||||
String clientId = app.getClientId();
|
||||
String authorizeUrl = authUrl + "/login/oauth/authorize";
|
||||
String url = UriComponentsBuilder.fromUriString(authorizeUrl)
|
||||
.queryParam("client_id", clientId)
|
||||
.queryParam("scope", "read:user user:email")
|
||||
.encode()
|
||||
.build()
|
||||
.toUriString();
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuthProcessResult process(OAuthProcessContext context) {
|
||||
OauthAppPojo authApp = oauthAppDao.selectByRegistrationId(context.getRegistrationId());
|
||||
String clientId = authApp.getClientId();
|
||||
String clientSecret = authApp.getClientSecret();
|
||||
String baseUrl = authApp.getResourceUrl();
|
||||
|
||||
Map<String, String[]> params = context.getCallbackParameters();
|
||||
String code = params.get("code")[0];
|
||||
JsonNode tokenNode = githubRemoteService.getToken(baseUrl, clientId, clientSecret, code)
|
||||
.get("access_token");
|
||||
if (tokenNode == null) {
|
||||
throw new DatabasirAuthenticationException(DomainErrors.NETWORK_ERROR.exception());
|
||||
}
|
||||
String accessToken = tokenNode.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();
|
||||
OAuthProcessResult result = new OAuthProcessResult();
|
||||
result.setEmail(email);
|
||||
result.setNickname(nickname);
|
||||
result.setUsername(email);
|
||||
result.setAvatar(avatar);
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package com.databasir.core.domain.app.handler;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class OAuthProcessContext {
|
||||
|
||||
private String registrationId;
|
||||
|
||||
@Builder.Default
|
||||
private Map<String, String[]> callbackParameters = new HashMap<>();
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package com.databasir.core.domain.app.handler;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class OAuthProcessResult {
|
||||
|
||||
private String email;
|
||||
|
||||
private String username;
|
||||
|
||||
private String nickname;
|
||||
|
||||
private String avatar;
|
||||
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
package com.databasir.core.domain.app.handler;
|
||||
|
||||
public interface OpenAuthHandler {
|
||||
|
||||
boolean support(String oauthAppType);
|
||||
|
||||
String authorization(String registrationId);
|
||||
|
||||
OAuthProcessResult process(OAuthProcessContext context);
|
||||
}
|
@@ -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;
|
@@ -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,36 @@ public class LoginService {
|
||||
.refreshTokenExpireAt(refreshTokenExpireAt)
|
||||
.build();
|
||||
}
|
||||
|
||||
public Optional<UserLoginResponse> 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<UserRolePojo> rolePojoList =
|
||||
userRoleDao.selectByUserIds(Collections.singletonList(user.getId()));
|
||||
List<UserLoginResponse.RoleResponse> 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;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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;
|
||||
|
||||
}
|
@@ -65,14 +65,14 @@ public class UserService {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void create(UserCreateRequest userCreateRequest) {
|
||||
public Integer create(UserCreateRequest userCreateRequest) {
|
||||
userDao.selectByEmailOrUsername(userCreateRequest.getUsername()).ifPresent(data -> {
|
||||
throw DomainErrors.USERNAME_OR_EMAIL_DUPLICATE.exception();
|
||||
});
|
||||
String hashedPassword = bCryptPasswordEncoder.encode(userCreateRequest.getPassword());
|
||||
UserPojo pojo = userPojoConverter.of(userCreateRequest, hashedPassword);
|
||||
try {
|
||||
userDao.insertAndReturnId(pojo);
|
||||
return userDao.insertAndReturnId(pojo);
|
||||
} catch (DuplicateKeyException e) {
|
||||
throw DomainErrors.USERNAME_OR_EMAIL_DUPLICATE.exception();
|
||||
}
|
||||
@@ -91,6 +91,21 @@ public class UserService {
|
||||
return userResponseConverter.detailResponse(pojo, roles, groupNameMapById);
|
||||
}
|
||||
|
||||
public Optional<UserDetailResponse> get(String email) {
|
||||
return userDao.selectByEmail(email)
|
||||
.map(user -> {
|
||||
List<UserRolePojo> roles = userRoleDao.selectByUserIds(Collections.singletonList(user.getId()));
|
||||
List<Integer> groupIds = roles.stream()
|
||||
.map(UserRolePojo::getGroupId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(toList());
|
||||
Map<Integer, String> 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);
|
||||
|
@@ -0,0 +1,34 @@
|
||||
package com.databasir.core.infrastructure.remote;
|
||||
|
||||
import com.databasir.core.infrastructure.remote.github.GithubApiClient;
|
||||
import com.databasir.core.infrastructure.remote.github.GithubOauthClient;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.jackson.JacksonConverterFactory;
|
||||
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class ClientConfig {
|
||||
|
||||
@Bean
|
||||
public GithubApiClient githubApiClient() {
|
||||
Retrofit retrofit = new Retrofit.Builder()
|
||||
.baseUrl("https://api.github.com")
|
||||
.addConverterFactory(JacksonConverterFactory.create())
|
||||
.build();
|
||||
return retrofit.create(GithubApiClient.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GithubOauthClient githubOauthClient() {
|
||||
Retrofit retrofit = new Retrofit.Builder()
|
||||
.baseUrl("https://github.com")
|
||||
.addConverterFactory(JacksonConverterFactory.create())
|
||||
.build();
|
||||
return retrofit.create(GithubOauthClient.class);
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package com.databasir.core.infrastructure.remote.github;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Header;
|
||||
import retrofit2.http.Headers;
|
||||
import retrofit2.http.Url;
|
||||
|
||||
public interface GithubApiClient {
|
||||
|
||||
@GET
|
||||
@Headers(value = {
|
||||
"Accept: application/json"
|
||||
})
|
||||
Call<JsonNode> getEmail(@Url String url, @Header("Authorization") String token);
|
||||
|
||||
@GET
|
||||
@Headers(value = {
|
||||
"Accept: application/json"
|
||||
})
|
||||
Call<JsonNode> getProfile(@Url String url, @Header("Authorization") String token);
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
package com.databasir.core.infrastructure.remote.github;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.Headers;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.QueryMap;
|
||||
import retrofit2.http.Url;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface GithubOauthClient {
|
||||
|
||||
@Headers(value = {
|
||||
"Accept: application/json"
|
||||
})
|
||||
@POST
|
||||
Call<JsonNode> getAccessToken(@Url String url, @QueryMap Map<String, String> request);
|
||||
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
package com.databasir.core.infrastructure.remote.github;
|
||||
|
||||
import com.databasir.common.SystemException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class GithubRemoteService {
|
||||
|
||||
private static final String TOKEN_PREFIX = "token ";
|
||||
|
||||
private final GithubApiClient githubApiClient;
|
||||
|
||||
private final GithubOauthClient githubOauthClient;
|
||||
|
||||
public JsonNode getToken(String baseUrl,
|
||||
String clientId,
|
||||
String clientSecret,
|
||||
String code) {
|
||||
TokenRequest request = TokenRequest.builder()
|
||||
.client_id(clientId)
|
||||
.client_secret(clientSecret)
|
||||
.code(code)
|
||||
.build();
|
||||
String path = "/login/oauth/access_token";
|
||||
String url = baseUrl + path;
|
||||
return execute(githubOauthClient.getAccessToken(url, request.toMap()));
|
||||
}
|
||||
|
||||
public JsonNode getEmail(String baseUrl, String token) {
|
||||
String path;
|
||||
if (baseUrl.contains("api.github.com")) {
|
||||
path = "/user/emails";
|
||||
} else {
|
||||
path = "/api/v3/user/emails";
|
||||
}
|
||||
String url = baseUrl + path;
|
||||
return execute(githubApiClient.getEmail(url, TOKEN_PREFIX + token));
|
||||
}
|
||||
|
||||
public JsonNode getProfile(String baseUrl, String token) {
|
||||
String path;
|
||||
if (baseUrl.contains("api.github.com")) {
|
||||
path = "/user";
|
||||
} else {
|
||||
path = "/api/v3/user";
|
||||
}
|
||||
String url = baseUrl + path;
|
||||
return execute(githubApiClient.getProfile(url, TOKEN_PREFIX + token));
|
||||
}
|
||||
|
||||
private <T> T execute(Call<T> call) {
|
||||
try {
|
||||
Response<T> response = call.execute();
|
||||
if (!response.isSuccessful()) {
|
||||
log.error("request error: " + call.request() + ", response = " + response);
|
||||
throw new SystemException("Call Remote Error");
|
||||
} else {
|
||||
T body = response.body();
|
||||
return body;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new SystemException("System Error", e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
package com.databasir.core.infrastructure.remote.github;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@SuppressWarnings("checkstyle:all")
|
||||
@Builder
|
||||
public class TokenRequest {
|
||||
|
||||
private String client_id;
|
||||
|
||||
private String client_secret;
|
||||
|
||||
private String code;
|
||||
|
||||
private String redirect_uri;
|
||||
|
||||
public Map<String, String> toMap() {
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
map.put("client_id", client_id);
|
||||
map.put("client_secret", client_secret);
|
||||
map.put("code", code);
|
||||
if (redirect_uri != null) {
|
||||
map.put("redirect_uri", redirect_uri);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user