feat: support oauth2 login

This commit is contained in:
vran 2022-03-01 23:27:10 +08:00
parent 18edc58d2f
commit c1ab379f31
23 changed files with 307 additions and 137 deletions

View File

@ -26,6 +26,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc' implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-quartz' implementation 'org.springframework.boot:spring-boot-starter-quartz'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.flywaydb:flyway-core' implementation 'org.flywaydb:flyway-core'

View File

@ -1,5 +1,6 @@
package com.databasir.api; package com.databasir.api;
import com.databasir.api.config.security.DatabasirUserDetails;
import com.databasir.common.DatabasirException; import com.databasir.common.DatabasirException;
import com.databasir.common.JsonData; import com.databasir.common.JsonData;
import com.databasir.common.exception.InvalidTokenException; 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.log.annotation.Operation;
import com.databasir.core.domain.login.data.AccessTokenRefreshRequest; import com.databasir.core.domain.login.data.AccessTokenRefreshRequest;
import com.databasir.core.domain.login.data.AccessTokenRefreshResponse; 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 com.databasir.core.domain.login.service.LoginService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -27,8 +28,6 @@ import java.util.Objects;
@Slf4j @Slf4j
public class LoginController { public class LoginController {
private final AuthenticationManager authenticationManager;
private final LoginService loginService; private final LoginService loginService;
@GetMapping(Routes.Login.LOGOUT) @GetMapping(Routes.Login.LOGOUT)
@ -39,8 +38,8 @@ public class LoginController {
} }
@PostMapping(Routes.Login.REFRESH_ACCESS_TOKEN) @PostMapping(Routes.Login.REFRESH_ACCESS_TOKEN)
public JsonData<AccessTokenRefreshResponse> refreshAccessTokens(@RequestBody @Valid public JsonData<AccessTokenRefreshResponse> refreshAccessTokens(@RequestBody
AccessTokenRefreshRequest request) { @Valid AccessTokenRefreshRequest request) {
try { try {
return JsonData.ok(loginService.refreshAccessTokens(request)); return JsonData.ok(loginService.refreshAccessTokens(request));
} catch (DatabasirException e) { } catch (DatabasirException e) {
@ -54,4 +53,13 @@ public class LoginController {
} }
} }
@GetMapping(Routes.Login.LOGIN_INFO)
public JsonData<UserLoginResponse> getUserLoginData() {
DatabasirUserDetails user = (DatabasirUserDetails) SecurityContextHolder.getContext()
.getAuthentication()
.getPrincipal();
Integer userId = user.getUserPojo().getId();
return JsonData.ok(loginService.getUserLoginData(userId));
}
} }

View File

@ -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<String> authorization(@PathVariable String registrationId) {
String authorization = oAuthHandler.authorization(registrationId);
return JsonData.ok(authorization);
}
@GetMapping("/oauth2/apps")
@ResponseBody
public JsonData<List<OAuthAppResponse>> listApps() {
return JsonData.ok(oAuthAppService.listAll());
}
}

View File

@ -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<String, String> 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<List<OAuthAppResponse>> listApps() {
return JsonData.ok(oAuthAppService.listAll());
}
}

View File

@ -102,6 +102,9 @@ public interface Routes {
String LOGOUT = "/logout"; String LOGOUT = "/logout";
String REFRESH_ACCESS_TOKEN = "/access_tokens"; String REFRESH_ACCESS_TOKEN = "/access_tokens";
String LOGIN_INFO = "/login_info";
} }
interface OperationLog { interface OperationLog {

View File

@ -1,6 +1,7 @@
package com.databasir.api.config; package com.databasir.api.config;
import com.databasir.api.Routes; import com.databasir.api.Routes;
import com.databasir.api.config.oauth2.DatabasirOauth2LoginFilter;
import com.databasir.api.config.security.*; import com.databasir.api.config.security.*;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;

View File

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

View File

@ -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<String, String[]> 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;
}
}

View File

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

View File

@ -2,11 +2,12 @@ package com.databasir.api.config.security;
import com.databasir.common.JsonData; import com.databasir.common.JsonData;
import com.databasir.core.domain.login.data.LoginKeyResponse; 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.login.service.LoginService;
import com.databasir.core.domain.user.data.UserLoginResponse;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -16,9 +17,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.ZoneId;
import java.util.List;
import java.util.stream.Collectors;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@ -35,28 +33,10 @@ public class DatabasirAuthenticationSuccessHandler implements AuthenticationSucc
DatabasirUserDetails user = (DatabasirUserDetails) authentication.getPrincipal(); DatabasirUserDetails user = (DatabasirUserDetails) authentication.getPrincipal();
response.setCharacterEncoding(StandardCharsets.UTF_8.name()); response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setContentType(MediaType.APPLICATION_JSON_VALUE); 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()); LoginKeyResponse loginKey = loginService.generate(user.getUserPojo().getId());
data.setAccessToken(loginKey.getAccessToken()); UserLoginResponse data = loginService.getUserLoginData(user.getUserPojo().getId())
long expireAt = loginKey.getAccessTokenExpireAt().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); .orElseThrow(() -> new CredentialsExpiredException("请重新登陆"));
data.setAccessTokenExpireAt(expireAt);
data.setRefreshToken(loginKey.getRefreshToken());
List<UserLoginResponse.RoleResponse> 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);
objectMapper.writeValue(response.getWriter(), JsonData.ok(data)); objectMapper.writeValue(response.getWriter(), JsonData.ok(data));
} }
} }

View File

@ -1,6 +1,5 @@
package com.databasir.api.config.security; package com.databasir.api.config.security;
import com.databasir.dao.impl.LoginDao;
import com.databasir.dao.impl.UserDao; import com.databasir.dao.impl.UserDao;
import com.databasir.dao.impl.UserRoleDao; import com.databasir.dao.impl.UserRoleDao;
import com.databasir.dao.tables.pojos.UserPojo; import com.databasir.dao.tables.pojos.UserPojo;
@ -22,8 +21,6 @@ public class DatabasirUserDetailService implements UserDetailsService {
private final UserRoleDao userRoleDao; private final UserRoleDao userRoleDao;
private final LoginDao loginDao;
@Override @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserPojo user = userDao.selectByEmailOrUsername(username) UserPojo user = userDao.selectByEmailOrUsername(username)

View File

@ -2,6 +2,8 @@ package com.databasir.common;
import lombok.Data; import lombok.Data;
import java.util.Optional;
@Data @Data
public class JsonData<T> { public class JsonData<T> {
@ -21,7 +23,7 @@ public class JsonData<T> {
private String errMessage; private String errMessage;
public static <T> JsonData<T> ok() { public static <T> JsonData<T> ok() {
return ok(null); return ok(Optional.empty());
} }
public static <T> JsonData<T> ok(T data) { public static <T> JsonData<T> ok(T data) {
@ -30,6 +32,13 @@ public class JsonData<T> {
return jsonData; return jsonData;
} }
public static <T> JsonData<T> ok(Optional<T> data) {
JsonData<T> jsonData = new JsonData<>();
jsonData.setData(data.orElse(null));
return jsonData;
}
public static <T> JsonData<T> error(String errorCode, String errMessage) { public static <T> JsonData<T> error(String errorCode, String errMessage) {
JsonData<T> jsonData = new JsonData<>(); JsonData<T> jsonData = new JsonData<>();
jsonData.setErrCode(errorCode); jsonData.setErrCode(errorCode);

View File

@ -1,4 +1,4 @@
package com.databasir.core.domain.user.data; package com.databasir.core.domain.login.data;
import lombok.Data; import lombok.Data;
@ -15,6 +15,8 @@ public class UserLoginResponse {
private String username; private String username;
private String avatar;
private String accessToken; private String accessToken;
private long accessTokenExpireAt; private long accessTokenExpireAt;

View File

@ -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.AccessTokenRefreshRequest;
import com.databasir.core.domain.login.data.AccessTokenRefreshResponse; import com.databasir.core.domain.login.data.AccessTokenRefreshResponse;
import com.databasir.core.domain.login.data.LoginKeyResponse; 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.core.infrastructure.jwt.JwtTokens;
import com.databasir.dao.impl.LoginDao; import com.databasir.dao.impl.LoginDao;
import com.databasir.dao.impl.UserDao; 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.LoginPojo;
import com.databasir.dao.tables.pojos.UserPojo; import com.databasir.dao.tables.pojos.UserPojo;
import com.databasir.dao.tables.pojos.UserRolePojo;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -16,8 +19,11 @@ import org.springframework.stereotype.Service;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.Collections;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@ -28,6 +34,8 @@ public class LoginService {
private final UserDao userDao; private final UserDao userDao;
private final UserRoleDao userRoleDao;
private final JwtTokens jwtTokens; private final JwtTokens jwtTokens;
public AccessTokenRefreshResponse refreshAccessTokens(AccessTokenRefreshRequest request) { public AccessTokenRefreshResponse refreshAccessTokens(AccessTokenRefreshRequest request) {
@ -91,4 +99,34 @@ public class LoginService {
.refreshTokenExpireAt(refreshTokenExpireAt) .refreshTokenExpireAt(refreshTokenExpireAt)
.build(); .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;
});
}
} }

View File

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

View File

@ -91,6 +91,21 @@ public class UserService {
return userResponseConverter.detailResponse(pojo, roles, groupNameMapById); 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 @Transactional
public String renewPassword(Integer userId) { public String renewPassword(Integer userId) {
UserPojo userPojo = userDao.selectById(userId); UserPojo userPojo = userDao.selectById(userId);

View File

@ -7,6 +7,8 @@ import com.databasir.dao.impl.OAuthAppDao;
import com.databasir.dao.tables.pojos.OauthAppPojo; import com.databasir.dao.tables.pojos.OauthAppPojo;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.jooq.tools.StringUtils;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
@ -47,17 +49,23 @@ public class GithubOauthHandler implements OAuthHandler {
String clientSecret = authApp.getClientSecret(); String clientSecret = authApp.getClientSecret();
String baseUrl = authApp.getResourceUrl(); String baseUrl = authApp.getResourceUrl();
Map<String, String> params = context.getCallbackParameters(); Map<String, String[]> params = context.getCallbackParameters();
String code = params.get("code"); String code = params.get("code")[0];
String accessToken = githubRemoteService.getToken(baseUrl, clientId, clientSecret, code) String accessToken = githubRemoteService.getToken(baseUrl, clientId, clientSecret, code)
.get("access_token") .get("access_token")
.asText(); .asText();
if (StringUtils.isBlank(accessToken)) {
throw new CredentialsExpiredException("授权失效,请重新登陆");
}
String email = null; String email = null;
for (JsonNode node : githubRemoteService.getEmail(baseUrl, accessToken)) { for (JsonNode node : githubRemoteService.getEmail(baseUrl, accessToken)) {
if (node.get("primary").asBoolean()) { if (node.get("primary").asBoolean()) {
email = node.get("email").asText(); email = node.get("email").asText();
} }
} }
if (StringUtils.isBlank(email)) {
throw new CredentialsExpiredException("授权失效,请重新登陆");
}
JsonNode profile = githubRemoteService.getProfile(baseUrl, accessToken); JsonNode profile = githubRemoteService.getProfile(baseUrl, accessToken);
String nickname = profile.get("name").asText(); String nickname = profile.get("name").asText();
String avatar = profile.get("avatar_url").asText(); String avatar = profile.get("avatar_url").asText();

View File

@ -1,18 +1,20 @@
package com.databasir.core.infrastructure.oauth2; 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.UserCreateRequest;
import com.databasir.core.domain.user.data.UserDetailResponse;
import com.databasir.core.domain.user.service.UserService; 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.core.infrastructure.oauth2.data.OAuthAppResponse;
import com.databasir.dao.impl.OAuthAppDao; import com.databasir.dao.impl.OAuthAppDao;
import com.databasir.dao.tables.pojos.OauthAppPojo; import com.databasir.dao.tables.pojos.OauthAppPojo;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@ -24,16 +26,16 @@ public class OAuthAppService {
private final UserService userService; private final UserService userService;
private final LoginService loginService; private final OAuthAppResponseConverter oAuthAppResponseConverter;
public LoginKeyResponse oauthCallback(String registrationId, Map<String, String> params) { public UserDetailResponse oauthCallback(String registrationId, Map<String, String[]> params) {
// match handler // match handler
OauthAppPojo app = oAuthAppDao.selectByRegistrationId(registrationId); OauthAppPojo app = oAuthAppDao.selectByRegistrationId(registrationId);
OAuthHandler oAuthHandler = oAuthHandlers.stream() OAuthHandler oAuthHandler = oAuthHandlers.stream()
.filter(handler -> handler.support(app.getAppType())) .filter(handler -> handler.support(app.getAppType()))
.findFirst() .findFirst()
.orElseThrow(); .orElseThrow(() -> new UsernameNotFoundException("暂不支持该类型登陆"));
// process by handler // process by handler
OAuthProcessContext context = OAuthProcessContext.builder() OAuthProcessContext context = OAuthProcessContext.builder()
@ -42,20 +44,25 @@ public class OAuthAppService {
.build(); .build();
OAuthProcessResult result = oAuthHandler.process(context); OAuthProcessResult result = oAuthHandler.process(context);
// create new user // get or create new user
UserCreateRequest user = new UserCreateRequest(); return userService.get(result.getEmail())
user.setUsername(result.getUsername()); .orElseGet(() -> {
user.setNickname(result.getNickname()); UserCreateRequest user = new UserCreateRequest();
user.setEmail(result.getEmail()); user.setUsername(result.getUsername());
user.setAvatar(result.getAvatar()); user.setNickname(result.getNickname());
user.setPassword(UUID.randomUUID().toString().substring(0, 6)); user.setEmail(result.getEmail());
Integer userId = userService.create(user); user.setAvatar(result.getAvatar());
user.setPassword(UUID.randomUUID().toString().substring(0, 6));
return loginService.generate(userId); Integer id = userService.create(user);
return userService.get(id);
});
} }
public List<OAuthAppResponse> listAll() { public List<OAuthAppResponse> listAll() {
return null; List<OauthAppPojo> apps = oAuthAppDao.selectAll();
return apps.stream()
.map(oAuthAppResponseConverter::to)
.collect(Collectors.toList());
} }
} }

View File

@ -17,6 +17,6 @@ public class OAuthProcessContext {
private String registrationId; private String registrationId;
@Builder.Default @Builder.Default
private Map<String, String> callbackParameters = new HashMap<>(); private Map<String, String[]> callbackParameters = new HashMap<>();
} }

View File

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

View File

@ -15,18 +15,14 @@ public class OAuthAppResponse {
private Integer id; private Integer id;
private String name; private String appName;
private String icon; private String appIcon;
private String appType;
private String registrationId; private String registrationId;
private String clientId;
private String clientSecret;
private LocalDateTime updateAt;
private LocalDateTime createAt; private LocalDateTime createAt;
} }

View File

@ -90,6 +90,12 @@ public abstract class BaseDao<R> {
.fetchInto(pojoType); .fetchInto(pojoType);
} }
public List<R> selectAll() {
return this.getDslContext()
.selectFrom(table())
.fetchInto(pojoType);
}
public Page<R> selectByPage(Pageable request, Condition condition) { public Page<R> selectByPage(Pageable request, Condition condition) {
Integer count = getDslContext() Integer count = getDslContext()
.selectCount().from(table).where(condition) .selectCount().from(table).where(condition)

View File

@ -40,6 +40,13 @@ public class LoginDao extends BaseDao<LoginPojo> {
.fetchOptionalInto(LoginPojo.class); .fetchOptionalInto(LoginPojo.class);
} }
public Optional<LoginPojo> 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) { public void updateAccessToken(String accessToken, LocalDateTime accessTokenExpireAt, Integer userId) {
getDslContext() getDslContext()
.update(LOGIN) .update(LOGIN)