feat: support oauth2 login
This commit is contained in:
parent
18edc58d2f
commit
c1ab379f31
|
@ -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'
|
||||
|
||||
|
|
|
@ -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<AccessTokenRefreshResponse> refreshAccessTokens(@RequestBody @Valid
|
||||
AccessTokenRefreshRequest request) {
|
||||
public JsonData<AccessTokenRefreshResponse> 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<UserLoginResponse> getUserLoginData() {
|
||||
DatabasirUserDetails user = (DatabasirUserDetails) SecurityContextHolder.getContext()
|
||||
.getAuthentication()
|
||||
.getPrincipal();
|
||||
Integer userId = user.getUserPojo().getId();
|
||||
return JsonData.ok(loginService.getUserLoginData(userId));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -102,6 +102,9 @@ public interface Routes {
|
|||
String LOGOUT = "/logout";
|
||||
|
||||
String REFRESH_ACCESS_TOKEN = "/access_tokens";
|
||||
|
||||
String LOGIN_INFO = "/login_info";
|
||||
|
||||
}
|
||||
|
||||
interface OperationLog {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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<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);
|
||||
UserLoginResponse data = loginService.getUserLoginData(user.getUserPojo().getId())
|
||||
.orElseThrow(() -> new CredentialsExpiredException("请重新登陆"));
|
||||
objectMapper.writeValue(response.getWriter(), JsonData.ok(data));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -2,6 +2,8 @@ package com.databasir.common;
|
|||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Data
|
||||
public class JsonData<T> {
|
||||
|
||||
|
@ -21,7 +23,7 @@ public class JsonData<T> {
|
|||
private String errMessage;
|
||||
|
||||
public static <T> JsonData<T> ok() {
|
||||
return ok(null);
|
||||
return ok(Optional.empty());
|
||||
}
|
||||
|
||||
public static <T> JsonData<T> ok(T data) {
|
||||
|
@ -30,6 +32,13 @@ public class JsonData<T> {
|
|||
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) {
|
||||
JsonData<T> jsonData = new JsonData<>();
|
||||
jsonData.setErrCode(errorCode);
|
||||
|
|
|
@ -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,34 @@ 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;
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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<String, String> params = context.getCallbackParameters();
|
||||
String code = params.get("code");
|
||||
Map<String, String[]> 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();
|
||||
|
|
|
@ -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<String, String> params) {
|
||||
public UserDetailResponse oauthCallback(String registrationId, Map<String, String[]> 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<OAuthAppResponse> listAll() {
|
||||
return null;
|
||||
List<OauthAppPojo> apps = oAuthAppDao.selectAll();
|
||||
return apps.stream()
|
||||
.map(oAuthAppResponseConverter::to)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,6 @@ public class OAuthProcessContext {
|
|||
private String registrationId;
|
||||
|
||||
@Builder.Default
|
||||
private Map<String, String> callbackParameters = new HashMap<>();
|
||||
private Map<String, String[]> callbackParameters = new HashMap<>();
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -90,6 +90,12 @@ public abstract class BaseDao<R> {
|
|||
.fetchInto(pojoType);
|
||||
}
|
||||
|
||||
public List<R> selectAll() {
|
||||
return this.getDslContext()
|
||||
.selectFrom(table())
|
||||
.fetchInto(pojoType);
|
||||
}
|
||||
|
||||
public Page<R> selectByPage(Pageable request, Condition condition) {
|
||||
Integer count = getDslContext()
|
||||
.selectCount().from(table).where(condition)
|
||||
|
|
|
@ -40,6 +40,13 @@ public class LoginDao extends BaseDao<LoginPojo> {
|
|||
.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) {
|
||||
getDslContext()
|
||||
.update(LOGIN)
|
||||
|
|
Loading…
Reference in New Issue